Make BackgroundDexOpt aware of thermal state

This change makes the BackgroundDexOpt service consider the thermal
state of the device before running.  If the device is in a moderate thermal
state or worse background dexopt will not run.

Bug: 165935246
Bug: 181795682
Test: Treehugger && atest BackgroundDexOptServiceIntegrationTests
Change-Id: Ie5ccbab7aa6d414241780136407f397d326340bf
This commit is contained in:
Chris Wailes 2020-12-04 09:53:49 -08:00 committed by Christian Wailes
parent 8a6ffdc597
commit 5d8c1369c1
3 changed files with 76 additions and 5 deletions

View File

@ -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<PackagesUpdatedListener> 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);

View File

@ -0,0 +1 @@
include platform/art:/OWNERS

View File

@ -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();
}
}
}