summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/pm/BackgroundDexOptService.java184
1 files changed, 151 insertions, 33 deletions
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index eae2eaaaecad..2ce2dfe407f5 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
+
import android.app.AlarmManager;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
@@ -23,6 +25,9 @@ import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
import android.os.ServiceManager;
import android.util.ArraySet;
import android.util.Log;
@@ -38,7 +43,9 @@ public class BackgroundDexOptService extends JobService {
static final long RETRY_LATENCY = 4 * AlarmManager.INTERVAL_HOUR;
- static final int BACKGROUND_DEXOPT_JOB = 800;
+ static final int JOB_IDLE_OPTIMIZE = 800;
+ static final int JOB_POST_BOOT_UPDATE = 801;
+
private static ComponentName sDexoptServiceName = new ComponentName(
"android",
BackgroundDexOptService.class.getName());
@@ -48,66 +55,177 @@ public class BackgroundDexOptService extends JobService {
*/
static final ArraySet<String> sFailedPackageNames = new ArraySet<String>();
- final AtomicBoolean mIdleTime = new AtomicBoolean(false);
+ /**
+ * Atomics set to true if the JobScheduler requests an abort.
+ */
+ final AtomicBoolean mAbortPostBootUpdate = new AtomicBoolean(false);
+ final AtomicBoolean mAbortIdleOptimization = new AtomicBoolean(false);
+
+ /**
+ * Atomic set to true if one job should exit early because another job was started.
+ */
+ final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
public static void schedule(Context context) {
JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
- JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName)
- .setRequiresDeviceIdle(true)
- .setRequiresCharging(true)
- .setPeriodic(TimeUnit.DAYS.toMillis(1))
- .build();
- js.schedule(job);
+
+ // Schedule a one-off job which scans installed packages and updates
+ // out-of-date oat files.
+ js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
+ .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
+ .setOverrideDeadline(TimeUnit.MINUTES.toMillis(1))
+ .build());
+
+ // Schedule a daily job which scans installed packages and compiles
+ // those with fresh profiling data.
+ js.schedule(new JobInfo.Builder(JOB_IDLE_OPTIMIZE, sDexoptServiceName)
+ .setRequiresDeviceIdle(true)
+ .setRequiresCharging(true)
+ .setPeriodic(TimeUnit.DAYS.toMillis(1))
+ .build());
+
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Jobs scheduled");
+ }
}
- @Override
- public boolean onStartJob(JobParameters params) {
- Log.i(TAG, "onStartJob");
- final PackageManagerService pm =
- (PackageManagerService)ServiceManager.getService("package");
+ // Returns the current battery level as a 0-100 integer.
+ private int getBatteryLevel() {
+ IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ Intent intent = registerReceiver(null, filter);
+ int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
- if (pm.isStorageLow()) {
- Log.i(TAG, "Low storage, skipping this run");
- return false;
+ if (level < 0 || scale <= 0) {
+ // Battery data unavailable. This should never happen, so assume the worst.
+ return 0;
}
- final ArraySet<String> pkgs = pm.getOptimizablePackages();
- if (pkgs == null || pkgs.isEmpty()) {
- Log.i(TAG, "No packages to optimize");
+
+ return (100 * level / scale);
+ }
+
+ private boolean runPostBootUpdate(final JobParameters jobParams,
+ final PackageManagerService pm, final ArraySet<String> pkgs) {
+ if (mExitPostBootUpdate.get()) {
+ // This job has already been superseded. Do not start it.
return false;
}
- final JobParameters jobParams = params;
- mIdleTime.set(true);
- new Thread("BackgroundDexOptService_DexOpter") {
+ // Load low battery threshold from the system config. This is a 0-100 integer.
+ final int lowBatteryThreshold = getResources().getInteger(
+ com.android.internal.R.integer.config_lowBatteryWarningLevel);
+
+ mAbortPostBootUpdate.set(false);
+ new Thread("BackgroundDexOptService_PostBootUpdate") {
@Override
public void run() {
for (String pkg : pkgs) {
- if (!mIdleTime.get()) {
- // Out of the idle state. Stop the compilation.
+ if (mAbortPostBootUpdate.get()) {
+ // JobScheduler requested an early abort.
+ return;
+ }
+ if (mExitPostBootUpdate.get()) {
+ // Different job, which supersedes this one, is running.
+ break;
+ }
+ if (getBatteryLevel() < lowBatteryThreshold) {
+ // Rather bail than completely drain the battery.
+ break;
+ }
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Updating package " + pkg);
+ }
+ // Update package if needed. Note that there can be no race between concurrent
+ // jobs because PackageDexOptimizer.performDexOpt is synchronized.
+ pm.performDexOpt(pkg,
+ /* instruction set */ null,
+ /* checkProfiles */ false,
+ PackageManagerService.REASON_BOOT,
+ /* force */ false);
+ }
+ // Ran to completion, so we abandon our timeslice and do not reschedule.
+ jobFinished(jobParams, /* reschedule */ false);
+ }
+ }.start();
+ return true;
+ }
+
+ private boolean runIdleOptimization(final JobParameters jobParams,
+ final PackageManagerService pm, final ArraySet<String> pkgs) {
+ // If post-boot update is still running, request that it exits early.
+ mExitPostBootUpdate.set(true);
+
+ mAbortIdleOptimization.set(false);
+ new Thread("BackgroundDexOptService_IdleOptimization") {
+ @Override
+ public void run() {
+ for (String pkg : pkgs) {
+ if (mAbortIdleOptimization.get()) {
+ // JobScheduler requested an early abort.
return;
}
if (sFailedPackageNames.contains(pkg)) {
- // skip previously failing package
+ // Skip previously failing package
continue;
}
- if (!pm.performDexOpt(pkg, /* instruction set */ null, /* checkProfiles */ true,
- PackageManagerService.REASON_BACKGROUND_DEXOPT, /* force */ false)) {
- // there was a problem running dexopt,
- // remember this so we do not keep retrying.
+ // Optimize package if needed. Note that there can be no race between
+ // concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
+ if (!pm.performDexOpt(pkg,
+ /* instruction set */ null,
+ /* checkProfiles */ true,
+ PackageManagerService.REASON_BACKGROUND_DEXOPT,
+ /* force */ false)) {
+ // Dexopt failed, remember this so we do not keep retrying.
sFailedPackageNames.add(pkg);
}
}
- // ran to completion, so we abandon our timeslice and do not reschedule
- jobFinished(jobParams, false);
+ // Ran to completion, so we abandon our timeslice and do not reschedule.
+ jobFinished(jobParams, /* reschedule */ false);
}
}.start();
return true;
}
@Override
+ public boolean onStartJob(JobParameters params) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "onStartJob");
+ }
+
+ 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 == null || pkgs.isEmpty()) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "No packages to optimize");
+ }
+ return false;
+ }
+
+ if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
+ return runPostBootUpdate(params, pm, pkgs);
+ } else {
+ return runIdleOptimization(params, pm, pkgs);
+ }
+ }
+
+ @Override
public boolean onStopJob(JobParameters params) {
- Log.i(TAG, "onIdleStop");
- mIdleTime.set(false);
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "onStopJob");
+ }
+
+ if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
+ mAbortPostBootUpdate.set(true);
+ } else {
+ mAbortIdleOptimization.set(true);
+ }
return false;
}
}