diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 77c1c1db2257..49a0a8827699 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -31,6 +31,9 @@ import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.os.BatteryManagerInternal; import android.os.Environment; +import android.os.IThermalService; +import android.os.PowerManager; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; @@ -82,10 +85,15 @@ public class BackgroundDexOptService extends JobService { private static final int OPTIMIZE_ABORT_BY_JOB_SCHEDULER = 2; // Optimizations should be aborted. No space left on device. private static final int OPTIMIZE_ABORT_NO_SPACE_LEFT = 3; + // Optimizations should be aborted. Thermal throttling level too high. + private static final int OPTIMIZE_ABORT_THERMAL = 4; // Used for calculating space threshold for downgrading unused apps. private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2; + // Thermal cutoff value used if one isn't defined by a system property. + private static final int THERMAL_CUTOFF_DEFAULT = PowerManager.THERMAL_STATUS_MODERATE; + /** * Set of failed packages remembered across job runs. */ @@ -107,8 +115,14 @@ public class BackgroundDexOptService extends JobService { private static final long mDowngradeUnusedAppsThresholdInMillis = getDowngradeUnusedAppsThresholdInMillis(); + private final IThermalService mThermalService = + IThermalService.Stub.asInterface( + ServiceManager.getService(Context.THERMAL_SERVICE)); + private static List sPackagesUpdatedListeners = new ArrayList<>(); + private int mThermalStatusCutoff = THERMAL_CUTOFF_DEFAULT; + public static void schedule(Context context) { if (isBackgroundDexoptDisabled()) { return; @@ -251,12 +265,18 @@ public class BackgroundDexOptService extends JobService { Slog.w(TAG, "Idle optimizations aborted because of space constraints."); } else if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { Slog.w(TAG, "Idle optimizations aborted by job scheduler."); + } else if (result == OPTIMIZE_ABORT_THERMAL) { + Slog.w(TAG, "Idle optimizations aborted by thermal throttling."); } else { Slog.w(TAG, "Idle optimizations ended with unexpected code: " + result); } - if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { + + if (result == OPTIMIZE_ABORT_THERMAL) { + // Abandon our timeslice and reschedule + jobFinished(jobParams, /* wantsReschedule */ true); + } else if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { // Abandon our timeslice and do not reschedule. - jobFinished(jobParams, /* reschedule */ false); + jobFinished(jobParams, /* wantsReschedule */ false); } } }.start(); @@ -542,6 +562,24 @@ public class BackgroundDexOptService extends JobService { // JobScheduler requested an early abort. return OPTIMIZE_ABORT_BY_JOB_SCHEDULER; } + + // Abort background dexopt if the device is in a moderate or stronger thermal throttling + // state. + try { + final int thermalStatus = mThermalService.getCurrentThermalStatus(); + + if (DEBUG) { + Log.i(TAG, "Thermal throttling status during bgdexopt: " + thermalStatus); + } + + if (thermalStatus >= mThermalStatusCutoff) { + return OPTIMIZE_ABORT_THERMAL; + } + } catch (RemoteException ex) { + // Because this is a intra-process Binder call it is impossible for a RemoteException + // to be raised. + } + long usableSpace = mDataDir.getUsableSpace(); if (usableSpace < lowStorageThreshold) { // Rather bail than completely fill up the disk. @@ -603,6 +641,9 @@ public class BackgroundDexOptService extends JobService { return false; } + mThermalStatusCutoff = + SystemProperties.getInt("dalvik.vm.dexopt.thermal-cutoff", THERMAL_CUTOFF_DEFAULT); + boolean result; if (params.getJobId() == JOB_POST_BOOT_UPDATE) { result = runPostBootUpdate(params, pm, pkgs); diff --git a/tests/BackgroundDexOptServiceIntegrationTests/OWNERS b/tests/BackgroundDexOptServiceIntegrationTests/OWNERS new file mode 100644 index 000000000000..3414a7469ac2 --- /dev/null +++ b/tests/BackgroundDexOptServiceIntegrationTests/OWNERS @@ -0,0 +1 @@ +include platform/art:/OWNERS diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java index e05816eb391f..90ddb6ffb34a 100644 --- a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java +++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java @@ -20,6 +20,7 @@ import android.app.AlarmManager; import android.content.Context; import android.os.Environment; import android.os.ParcelFileDescriptor; +import android.os.PowerManager; import android.os.SystemProperties; import android.os.storage.StorageManager; import android.util.Log; @@ -201,11 +202,16 @@ public final class BackgroundDexOptServiceIntegrationTests { fillUpStorage((long) (getStorageLowBytes() * LOW_STORAGE_MULTIPLIER)); } - // TODO(aeubanks): figure out how to get scheduled bg-dexopt to run private static void runBackgroundDexOpt() throws IOException { + runBackgroundDexOpt("Success"); + } + + // TODO(aeubanks): figure out how to get scheduled bg-dexopt to run + private static void runBackgroundDexOpt(String expectedStatus) throws IOException { String result = runShellCommand("cmd package bg-dexopt-job " + PACKAGE_NAME); - if (!result.trim().equals("Success")) { - throw new IllegalStateException("Expected command success, received >" + result + "<"); + if (!result.trim().equals(expectedStatus)) { + throw new IllegalStateException("Expected status: " + expectedStatus + + "; Received: " + result.trim()); } } @@ -242,6 +248,16 @@ public final class BackgroundDexOptServiceIntegrationTests { runShellCommand(String.format("cmd package compile -f -m %s %s", filter, pkg)); } + // Override the thermal status of the device + public static void overrideThermalStatus(int status) throws IOException { + runShellCommand("cmd thermalservice override-status " + status); + } + + // Reset the thermal status of the device + public static void resetThermalStatus() throws IOException { + runShellCommand("cmd thermalservice reset"); + } + // Test that background dexopt under normal conditions succeeds. @Test public void testBackgroundDexOpt() throws IOException { @@ -307,4 +323,17 @@ public final class BackgroundDexOptServiceIntegrationTests { } } + // Test that background dexopt job doesn't trigger if the device is under thermal throttling. + @Test + public void testBackgroundDexOptThermalThrottling() throws IOException { + try { + compilePackageWithFilter(PACKAGE_NAME, "verify"); + overrideThermalStatus(PowerManager.THERMAL_STATUS_MODERATE); + // The bgdexopt task should fail when onStartJob is run + runBackgroundDexOpt("Failure"); + Assert.assertEquals("verify", getCompilerFilter(PACKAGE_NAME)); + } finally { + resetThermalStatus(); + } + } }