From 7ca1abd367c5a5fac6b68900c83043f4d5271ae7 Mon Sep 17 00:00:00 2001 From: Felix Lopez Luis Date: Wed, 12 Dec 2018 10:32:32 +0000 Subject: Add new atoms to log Downgraded Apps and Low Storage Merged-In: I211696c836cb9c8a7b72e3693ecba3061ca599bf Exempt-From-Owner-Approval: Cherry-pick Change-Id: I211696c836cb9c8a7b72e3693ecba3061ca599bf Bug-Id: b/120911106 Test: refactoring CL. Existing unit tests still pass. --- cmds/statsd/src/atoms.proto | 44 ++++++++++++++++ .../android/server/pm/BackgroundDexOptService.java | 59 +++++++++++++++++++++- .../storage/DeviceStorageMonitorService.java | 9 +++- 3 files changed, 108 insertions(+), 4 deletions(-) diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 6506df2ade75..a79fe775e07b 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -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; @@ -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: diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index d6ab5f717568..65fc9824c76e 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -27,24 +27,29 @@ 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; /** * {@hide} @@ -289,6 +294,50 @@ public class BackgroundDexOptService extends JobService { 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 pkgs, long lowStorageThreshold, boolean is_for_primary_dex, ArraySet failedPackageNames) { @@ -315,8 +364,10 @@ public class BackgroundDexOptService extends JobService { int reason; boolean downgrade; + long package_size_before = 0; //used when the app is downgraded // Downgrade unused packages. if (unusedPackages.contains(pkg) && shouldDowngrade) { + package_size_before = getPackageSize(pm, pkg); // 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)) { @@ -366,6 +417,10 @@ public class BackgroundDexOptService extends JobService { synchronized (failedPackageNames) { failedPackageNames.remove(pkg); } + if (downgrade) { + StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before, + getPackageSize(pm, pkg), /*aggressive=*/ false); + } } } notifyPinService(updatedPackages); diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java index f7cc4432f9bc..2700f9ddc203 100644 --- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java +++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java @@ -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; @@ -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; @@ -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); } } -- cgit v1.2.3-59-g8ed1b From 4bf2be95288d142cbf533f67fdc49361c733583d Mon Sep 17 00:00:00 2001 From: Felix Lopez Luis Date: Tue, 11 Dec 2018 14:07:24 +0000 Subject: Decouple downgrade and optimization processes. Extract the logic to downgrade apps from the logic to optimize apps. This will make easier to add more logic for downgrade applications as required for b/112144519 Bug: 112144519 Test: This change is cover by the integration tests in BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java Merged-In: Ia3ec6198bd8bac9871372ae87994ca0c596e36d7 Change-Id: Ia3ec6198bd8bac9871372ae87994ca0c596e36d7 --- .../android/server/pm/BackgroundDexOptService.java | 202 +++++++++++++-------- 1 file changed, 129 insertions(+), 73 deletions(-) diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 65fc9824c76e..ad9ac1232437 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -50,6 +50,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; /** * {@hide} @@ -57,7 +58,7 @@ import java.util.concurrent.atomic.AtomicBoolean; 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; @@ -102,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(); @@ -275,21 +275,18 @@ 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; } @@ -339,92 +336,84 @@ public class BackgroundDexOptService extends JobService { } private int optimizePackages(PackageManagerService pm, ArraySet pkgs, - long lowStorageThreshold, boolean is_for_primary_dex, - ArraySet failedPackageNames) { + long lowStorageThreshold, boolean isForPrimaryDex) { ArraySet updatedPackages = new ArraySet<>(); Set 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 - continue; - } - } - - int reason; - boolean downgrade; - long package_size_before = 0; //used when the app is downgraded // Downgrade unused packages. if (unusedPackages.contains(pkg) && shouldDowngrade) { - package_size_before = getPackageSize(pm, pkg); - // 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); + dex_opt_performed = downgradePackage(pm, pkg, isForPrimaryDex); + } else { + if (abort_code == OPTIMIZE_ABORT_NO_SPACE_LEFT) { + // can't dexopt because of low space. 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; + dex_opt_performed = optimizePackage(pm, pkg, isForPrimaryDex); } - - synchronized (failedPackageNames) { - // Conservatively add package to the list of failing ones in case - // performDexOpt never returns. - failedPackageNames.add(pkg); + if (dex_opt_performed) { + updatedPackages.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); - } + 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 (!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); } 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 (downgrade) { - StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before, - getPackageSize(pm, pkg), /*aggressive=*/ false); - } + dex_opt_performed = performDexOptPrimary(pm, pkg, reason, dexoptFlags); } + } else { + dex_opt_performed = performDexOptSecondary(pm, pkg, reason, dexoptFlags); } - notifyPinService(updatedPackages); - return OPTIMIZE_PROCESSED; + + if (dex_opt_performed) { + StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before, + getPackageSize(pm, pkg), /*aggressive=*/ false); + } + return dex_opt_performed; + } + + private boolean supportSecondaryDex() { + return (SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)); } private int reconcileSecondaryDexFiles(DexManager dm) { @@ -438,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 performDexOptWrapper) { + ArraySet 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()) { -- cgit v1.2.3-59-g8ed1b