diff options
author | Carmen Agimof <carmenagimof@google.com> | 2019-11-22 13:48:32 +0000 |
---|---|---|
committer | Carmen Agimof <carmenagimof@google.com> | 2019-11-22 14:59:14 +0000 |
commit | 1ef924bfeb721dde5a8e6aab2649474e37eb484a (patch) | |
tree | fd8d099886e87303bcf1298202ce03ce53c66bc1 | |
parent | bdcc111625a5d070ee3525d6f04904dc5bd33ca3 (diff) |
Revert "Downgrade the compilation filter of unused apps."
This reverts commit bdcc111625a5d070ee3525d6f04904dc5bd33ca3.
Reason for revert: It has been decided to not move forward with this experiment.
Change-Id: If52a2bb8ba57fd60ba736a5d23a254e72d78645b
3 files changed, 44 insertions, 274 deletions
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 08e55d3b766b..c7124314cae0 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -34,8 +34,6 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.StorageManager; -import android.provider.DeviceConfig; -import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import android.util.StatsLog; @@ -86,12 +84,6 @@ public class BackgroundDexOptService extends JobService { // Used for calculating space threshold for downgrading unused apps. private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2; - private static final int DEFAULT_INACTIVE_APP_THRESHOLD_DAYS = 10; - - private static final String DOWNGRADE_UNUSED_APPS_ENABLED = "downgrade_unused_apps_enabled"; - private static final String INACTIVE_APP_THRESHOLD_DAYS = "inactive_app_threshold_days"; - private static final String LOW_STORAGE_MULTIPLIER_FOR_DOWNGRADE = - "low_storage_threshold_multiplier_for_downgrade"; /** * Set of failed packages remembered across job runs. @@ -111,6 +103,8 @@ public class BackgroundDexOptService extends JobService { private final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false); private final File mDataDir = Environment.getDataDirectory(); + private static final long mDowngradeUnusedAppsThresholdInMillis = + getDowngradeUnusedAppsThresholdInMillis(); public static void schedule(Context context) { if (isBackgroundDexoptDisabled()) { @@ -352,14 +346,14 @@ public class BackgroundDexOptService extends JobService { // 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 = getLowThresholdMultiplierForDowngrade() + final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE * lowStorageThreshold; boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade); Log.d(TAG, "Should Downgrade " + shouldDowngrade); if (shouldDowngrade) { Set<String> unusedPackages = - pm.getUnusedPackages(getDowngradeUnusedAppsThresholdInMillis()); - Log.d(TAG, "Unused Packages " + String.join(",", unusedPackages)); + pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis); + Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages)); if (!unusedPackages.isEmpty()) { for (String pkg : unusedPackages) { @@ -368,9 +362,12 @@ public class BackgroundDexOptService extends JobService { // Should be aborted by the scheduler. return abortCode; } - if (downgradePackage(pm, pkg)) { + if (downgradePackage(pm, pkg, /*isForPrimaryDex*/ true)) { updatedPackages.add(pkg); } + if (supportSecondaryDex) { + downgradePackage(pm, pkg, /*isForPrimaryDex*/ false); + } } pkgs = new ArraySet<>(pkgs); @@ -418,45 +415,39 @@ public class BackgroundDexOptService extends JobService { * 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 - * @return true if the package was downgraded + * @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) { + private boolean downgradePackage(PackageManagerService pm, String pkg, + boolean isForPrimaryDex) { Log.d(TAG, "Downgrading " + pkg); - boolean downgradedPrimary = false; + 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); - // An aggressive downgrade deletes the oat files. - boolean aggressive = false; - // 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); - aggressive = true; - downgradedPrimary = true; - } else { - downgradedPrimary = performDexOptPrimary(pm, pkg, reason, dexoptFlags); - if (supportSecondaryDex()) { - performDexOptSecondary(pm, pkg, reason, dexoptFlags); + 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 { + dex_opt_performed = performDexOptPrimary(pm, pkg, reason, dexoptFlags); } + } else { + dex_opt_performed = performDexOptSecondary(pm, pkg, reason, dexoptFlags); } - // This metric aims to log the storage savings when downgrading. - // The way disk size is measured using getPackageSize only looks at the primary apks. - // Any logs that are due to secondary dex files will show 0% size reduction and pollute - // the metrics. - if (downgradedPrimary) { + if (dex_opt_performed) { StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before, - getPackageSize(pm, pkg), aggressive); + getPackageSize(pm, pkg), /*aggressive=*/ false); } - return downgradedPrimary; + return dex_opt_performed; } private boolean supportSecondaryDex() { @@ -480,7 +471,7 @@ public class BackgroundDexOptService extends JobService { * 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. + * @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, @@ -597,6 +588,12 @@ public class BackgroundDexOptService extends JobService { // the checks above. This check is not "live" - the value is determined by a background // restart with a period of ~1 minute. PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package"); + if (pm.isStorageLow()) { + if (DEBUG_DEXOPT) { + Log.i(TAG, "Low storage, skipping this run"); + } + return false; + } final ArraySet<String> pkgs = pm.getOptimizablePackages(); if (pkgs.isEmpty()) { @@ -646,77 +643,17 @@ public class BackgroundDexOptService extends JobService { } private static long getDowngradeUnusedAppsThresholdInMillis() { - long defaultValue = Long.MAX_VALUE; - if (isDowngradeFeatureEnabled()) { - return getInactiveAppsThresholdMillis(); - } 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 defaultValue; - } - try { - return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue)); - } catch (NumberFormatException e) { - Log.w(TAG, "Couldn't parse long for pm.dexopt.downgrade_after_inactive_days: " - + sysPropValue + ". Returning default value instead."); - return defaultValue; + return Long.MAX_VALUE; } + return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue)); } private static boolean isBackgroundDexoptDisabled() { return SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt" /* key */, false /* default */); } - - private static boolean isDowngradeFeatureEnabled() { - // DeviceConfig enables the control of on device features via remotely configurable flags, - // compared to SystemProperties which is only a way of sharing info system-widely, but are - // not configurable on the server-side. - String downgradeUnusedAppsEnabledFlag = - DeviceConfig.getProperty( - DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, - DOWNGRADE_UNUSED_APPS_ENABLED); - return !TextUtils.isEmpty(downgradeUnusedAppsEnabledFlag) - && Boolean.parseBoolean(downgradeUnusedAppsEnabledFlag); - } - - private static long getInactiveAppsThresholdMillis() { - long defaultValue = TimeUnit.DAYS.toMillis(DEFAULT_INACTIVE_APP_THRESHOLD_DAYS); - String inactiveAppThresholdDaysFlag = - DeviceConfig.getProperty(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, - INACTIVE_APP_THRESHOLD_DAYS); - if (!TextUtils.isEmpty(inactiveAppThresholdDaysFlag)) { - try { - return TimeUnit.DAYS.toMillis(Long.parseLong(inactiveAppThresholdDaysFlag)); - } catch (NumberFormatException e) { - Log.w(TAG, "Couldn't parse long for " + INACTIVE_APP_THRESHOLD_DAYS + " flag: " - + inactiveAppThresholdDaysFlag + ". Returning default value instead."); - return defaultValue; - } - } - return defaultValue; - } - - private static int getLowThresholdMultiplierForDowngrade() { - int defaultValue = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE; - if (isDowngradeFeatureEnabled()) { - String lowStorageThresholdMultiplierFlag = - DeviceConfig.getProperty(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, - LOW_STORAGE_MULTIPLIER_FOR_DOWNGRADE); - if (!TextUtils.isEmpty(lowStorageThresholdMultiplierFlag)) { - try { - return Integer.parseInt(lowStorageThresholdMultiplierFlag); - } catch (NumberFormatException e) { - Log.w(TAG, "Couldn't parse long for " - + LOW_STORAGE_MULTIPLIER_FOR_DOWNGRADE + " flag: " - + lowStorageThresholdMultiplierFlag - + ". Returning default value instead."); - return defaultValue; - } - } - } - return defaultValue; - } } diff --git a/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml b/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml index 7291dc729541..aec9f77cf922 100644 --- a/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml +++ b/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml @@ -28,8 +28,6 @@ <uses-permission android:name="android.permission.SET_TIME" /> <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> - <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" /> - <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <application> <uses-library android:name="android.test.runner" /> diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java index 4cd56c3c42be..7d826f7172da 100644 --- a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java +++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java @@ -22,7 +22,6 @@ import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.SystemProperties; import android.os.storage.StorageManager; -import android.provider.DeviceConfig; import android.util.Log; import androidx.test.InstrumentationRegistry; @@ -31,9 +30,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -55,13 +52,6 @@ import java.util.concurrent.TimeUnit; * 3. Under low storage conditions and package is recently used, check * that dexopt upgrades test app to $(getprop pm.dexopt.bg-dexopt). * - * When downgrade feature is on (downgrade_unused_apps_enabled flag is set to true): - * 4 On low storage, check that the inactive packages are downgraded. - * 5. On low storage, check that used packages are upgraded. - * 6. On storage completely full, dexopt fails. - * 7. Not on low storage, unused packages are upgraded. - * 8. Low storage, unused app is downgraded. When app is used again, app is upgraded. - * * Each test case runs "cmd package bg-dexopt-job com.android.frameworks.bgdexopttest". * * The setup for these tests make sure this package has been configured to have been recently used @@ -69,10 +59,6 @@ import java.util.concurrent.TimeUnit; * recently used, it sets the time forward more than * `getprop pm.dexopt.downgrade_after_inactive_days` days. * - * For some of the tests, the DeviceConfig flags inactive_app_threshold_days and - * downgrade_unused_apps_enabled are set. These turn on/off the downgrade unused apps feature for - * all devices and set the time threshold for unused apps. - * * For tests that require low storage, the phone is filled up. * * Run with "atest BackgroundDexOptServiceIntegrationTests". @@ -94,14 +80,10 @@ public final class BackgroundDexOptServiceIntegrationTests { "pm.dexopt.downgrade_after_inactive_days", 0); // Needs to be between 1.0 and 2.0. private static final double LOW_STORAGE_MULTIPLIER = 1.5; - private static final int DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS = 15; // The file used to fill up storage. private File mBigFile; - @Rule - public ExpectedException mExpectedException = ExpectedException.none(); - // Remember start time. @BeforeClass public static void setUpAll() { @@ -214,27 +196,11 @@ public final class BackgroundDexOptServiceIntegrationTests { logSpaceRemaining(); } - private void fillUpStorageCompletely() throws IOException { - fillUpStorage((getStorageLowBytes())); - } - // Fill up storage so that device is in low storage condition. private void fillUpToLowStorage() throws IOException { fillUpStorage((long) (getStorageLowBytes() * LOW_STORAGE_MULTIPLIER)); } - private void setInactivePackageThreshold(int threshold) { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, - "inactive_app_threshold_days", Integer.toString(threshold), false); - } - - private void enableDowngradeFeature(boolean enabled) { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, - "downgrade_unused_apps_enabled", Boolean.toString(enabled), false); - } - // TODO(aeubanks): figure out how to get scheduled bg-dexopt to run private static void runBackgroundDexOpt() throws IOException { String result = runShellCommand("cmd package bg-dexopt-job " + PACKAGE_NAME); @@ -278,7 +244,7 @@ public final class BackgroundDexOptServiceIntegrationTests { // Test that background dexopt under normal conditions succeeds. @Test - public void testBackgroundDexOpt_normalConditions_dexOptSucceeds() throws IOException { + public void testBackgroundDexOpt() throws IOException { // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "verify"); Assert.assertEquals("verify", getCompilerFilter(PACKAGE_NAME)); @@ -291,16 +257,17 @@ public final class BackgroundDexOptServiceIntegrationTests { // Test that background dexopt under low storage conditions upgrades used packages. @Test - public void testBackgroundDexOpt_lowStorage_usedPkgsUpgraded() throws IOException { + public void testBackgroundDexOptDowngradeSkipRecentlyUsedPackage() throws IOException { // Should be less than DOWNGRADE_AFTER_DAYS. long deltaDays = DOWNGRADE_AFTER_DAYS - 1; try { - enableDowngradeFeature(false); // Set time to future. setTimeFutureDays(deltaDays); + // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); + // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); @@ -315,161 +282,29 @@ public final class BackgroundDexOptServiceIntegrationTests { } // Test that background dexopt under low storage conditions downgrades unused packages. - // This happens if the system property pm.dexopt.downgrade_after_inactive_days is set - // (e.g. on Android Go devices). @Test - public void testBackgroundDexOpt_lowStorage_unusedPkgsDowngraded() - throws IOException { + public void testBackgroundDexOptDowngradeSuccessful() throws IOException { // Should be more than DOWNGRADE_AFTER_DAYS. long deltaDays = DOWNGRADE_AFTER_DAYS + 1; try { - enableDowngradeFeature(false); - // Set time to future. - setTimeFutureDays(deltaDays); - // Set filter to quicken. - compilePackageWithFilter(PACKAGE_NAME, "quicken"); - Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); - // Fill up storage to trigger low storage threshold. - fillUpToLowStorage(); - - runBackgroundDexOpt(); - - // Verify that downgrade is successful. - Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); - } finally { - // Reset time. - setTimeFutureDays(-deltaDays); - } - } - - // Test that the background dexopt downgrades inactive packages when the downgrade feature is - // enabled. - @Test - public void testBackgroundDexOpt_downgradeFeatureEnabled_lowStorage_inactivePkgsDowngraded() - throws IOException { - // Should be more than DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS. - long deltaDays = DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS + 1; - try { - enableDowngradeFeature(true); - setInactivePackageThreshold(DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS); // Set time to future. setTimeFutureDays(deltaDays); - // Set filter to quicken. - compilePackageWithFilter(PACKAGE_NAME, "quicken"); - Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); - // Fill up storage to trigger low storage threshold. - fillUpToLowStorage(); - - runBackgroundDexOpt(); - - // Verify that downgrade is successful. - Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); - } finally { - // Reset time. - setTimeFutureDays(-deltaDays); - } - } - - // Test that the background dexopt upgrades used packages when the downgrade feature is enabled. - // This test doesn't fill the device storage completely, but to a multiplier of the low storage - // threshold and this is why apps can still be optimized. - @Test - public void testBackgroundDexOpt_downgradeFeatureEnabled_lowStorage_usedPkgsUpgraded() - throws IOException { - enableDowngradeFeature(true); - // Set filter to quicken. - compilePackageWithFilter(PACKAGE_NAME, "quicken"); - Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); - // Fill up storage to trigger low storage threshold. - fillUpToLowStorage(); - - runBackgroundDexOpt(); - - /// Verify that bg-dexopt is successful in upgrading the used packages. - Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); - } - - // Test that the background dexopt fails and doesn't change the compilation filter of used - // packages when the downgrade feature is enabled and the storage is filled up completely. - // The bg-dexopt shouldn't optimise nor downgrade these packages. - @Test - public void testBackgroundDexOpt_downgradeFeatureEnabled_fillUpStorageCompletely_dexOptFails() - throws IOException { - enableDowngradeFeature(true); - String previousCompilerFilter = getCompilerFilter(PACKAGE_NAME); - - // Fill up storage completely, without using a multiplier for the low storage threshold. - fillUpStorageCompletely(); - - // When the bg dexopt runs with the storage filled up completely, it will fail. - mExpectedException.expect(IllegalStateException.class); - runBackgroundDexOpt(); - - /// Verify that bg-dexopt doesn't change the compilation filter of used apps. - Assert.assertEquals(previousCompilerFilter, getCompilerFilter(PACKAGE_NAME)); - } - // Test that the background dexopt upgrades the unused packages when the downgrade feature is - // on if the device is not low on storage. - @Test - public void testBackgroundDexOpt_downgradeFeatureEnabled_notLowStorage_unusedPkgsUpgraded() - throws IOException { - // Should be more than DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS. - long deltaDays = DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS + 1; - try { - enableDowngradeFeature(true); - setInactivePackageThreshold(DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS); - // Set time to future. - setTimeFutureDays(deltaDays); // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); - runBackgroundDexOpt(); - - // Verify that bg-dexopt is successful in upgrading the unused packages when the device - // is not low on storage. - Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); - } finally { - // Reset time. - setTimeFutureDays(-deltaDays); - } - } - - // Test that when an unused package (which was downgraded) is used again, it's re-optimized when - // bg-dexopt runs again. - @Test - public void testBackgroundDexOpt_downgradeFeatureEnabled_downgradedPkgsUpgradedAfterUse() - throws IOException { - // Should be more than DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS. - long deltaDays = DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS + 1; - try { - enableDowngradeFeature(true); - setInactivePackageThreshold(DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS); - // Set time to future. - setTimeFutureDays(deltaDays); // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); - // Set filter to quicken. - compilePackageWithFilter(PACKAGE_NAME, "quicken"); - Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); runBackgroundDexOpt(); // Verify that downgrade is successful. Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); - - // Reset time. - setTimeFutureDays(-deltaDays); - deltaDays = 0; - runBackgroundDexOpt(); - - // Verify that bg-dexopt is successful in upgrading the unused packages that were used - // again. - Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); } finally { // Reset time. setTimeFutureDays(-deltaDays); } } + } |