diff options
| -rw-r--r-- | services/core/java/com/android/server/pm/BackgroundDexOptService.java | 184 |
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; } } |