Loading services/core/java/com/android/server/pm/BackgroundDexOptService.java +155 −34 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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()); Loading @@ -48,66 +55,180 @@ 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) // 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(); js.schedule(job); .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. // Conservatively add package to the list of failing ones in case performDexOpt // never returns. sFailedPackageNames.add(pkg); // 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 succeeded, remove package from the list of failing ones. sFailedPackageNames.remove(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; } } services/core/java/com/android/server/pm/PackageDexOptimizer.java +12 −6 Original line number Diff line number Diff line Loading @@ -53,7 +53,6 @@ class PackageDexOptimizer { // TODO b/19550105 Remove error codes and use exceptions static final int DEX_OPT_SKIPPED = 0; static final int DEX_OPT_PERFORMED = 1; static final int DEX_OPT_DEFERRED = 2; static final int DEX_OPT_FAILED = -1; private final Installer mInstaller; Loading Loading @@ -170,6 +169,8 @@ class PackageDexOptimizer { final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; boolean performedDexOpt = false; boolean successfulDexOpt = true; final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); for (String dexCodeInstructionSet : dexCodeInstructionSets) { for (String path : paths) { Loading Loading @@ -226,15 +227,20 @@ class PackageDexOptimizer { performedDexOpt = true; } catch (InstallerException e) { Slog.w(TAG, "Failed to dexopt", e); successfulDexOpt = false; } } } // If we've gotten here, we're sure that no error occurred and that we haven't // deferred dex-opt. We've either dex-opted one more paths or instruction sets or // we've skipped all of them because they are up to date. In both cases this // package doesn't need dexopt any longer. if (successfulDexOpt) { // If we've gotten here, we're sure that no error occurred. We've either // dex-opted one or more paths or instruction sets or we've skipped // all of them because they are up to date. In both cases this package // doesn't need dexopt any longer. return performedDexOpt ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED; } else { return DEX_OPT_FAILED; } } /** Loading services/core/java/com/android/server/pm/PackageManagerService.java +3 −1 Original line number Diff line number Diff line Loading @@ -7153,6 +7153,8 @@ public class PackageManagerService extends IPackageManager.Stub { } } // Run dexopt on a given package. Returns true if dexopt did not fail, i.e. // if the package can now be considered up to date for the given filter. private boolean performDexOptInternal(String packageName, String instructionSet, boolean checkProfiles, String targetCompilerFilter, boolean force) { PackageParser.Package p; Loading @@ -7173,7 +7175,7 @@ public class PackageManagerService extends IPackageManager.Stub { final String[] instructionSets = new String[] { targetInstructionSet }; int result = performDexOptInternalWithDependenciesLI(p, instructionSets, checkProfiles, targetCompilerFilter, force); return result == PackageDexOptimizer.DEX_OPT_PERFORMED; return result != PackageDexOptimizer.DEX_OPT_FAILED; } } finally { Binder.restoreCallingIdentity(callingId); Loading Loading
services/core/java/com/android/server/pm/BackgroundDexOptService.java +155 −34 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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()); Loading @@ -48,66 +55,180 @@ 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) // 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(); js.schedule(job); .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. // Conservatively add package to the list of failing ones in case performDexOpt // never returns. sFailedPackageNames.add(pkg); // 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 succeeded, remove package from the list of failing ones. sFailedPackageNames.remove(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; } }
services/core/java/com/android/server/pm/PackageDexOptimizer.java +12 −6 Original line number Diff line number Diff line Loading @@ -53,7 +53,6 @@ class PackageDexOptimizer { // TODO b/19550105 Remove error codes and use exceptions static final int DEX_OPT_SKIPPED = 0; static final int DEX_OPT_PERFORMED = 1; static final int DEX_OPT_DEFERRED = 2; static final int DEX_OPT_FAILED = -1; private final Installer mInstaller; Loading Loading @@ -170,6 +169,8 @@ class PackageDexOptimizer { final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; boolean performedDexOpt = false; boolean successfulDexOpt = true; final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); for (String dexCodeInstructionSet : dexCodeInstructionSets) { for (String path : paths) { Loading Loading @@ -226,15 +227,20 @@ class PackageDexOptimizer { performedDexOpt = true; } catch (InstallerException e) { Slog.w(TAG, "Failed to dexopt", e); successfulDexOpt = false; } } } // If we've gotten here, we're sure that no error occurred and that we haven't // deferred dex-opt. We've either dex-opted one more paths or instruction sets or // we've skipped all of them because they are up to date. In both cases this // package doesn't need dexopt any longer. if (successfulDexOpt) { // If we've gotten here, we're sure that no error occurred. We've either // dex-opted one or more paths or instruction sets or we've skipped // all of them because they are up to date. In both cases this package // doesn't need dexopt any longer. return performedDexOpt ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED; } else { return DEX_OPT_FAILED; } } /** Loading
services/core/java/com/android/server/pm/PackageManagerService.java +3 −1 Original line number Diff line number Diff line Loading @@ -7153,6 +7153,8 @@ public class PackageManagerService extends IPackageManager.Stub { } } // Run dexopt on a given package. Returns true if dexopt did not fail, i.e. // if the package can now be considered up to date for the given filter. private boolean performDexOptInternal(String packageName, String instructionSet, boolean checkProfiles, String targetCompilerFilter, boolean force) { PackageParser.Package p; Loading @@ -7173,7 +7175,7 @@ public class PackageManagerService extends IPackageManager.Stub { final String[] instructionSets = new String[] { targetInstructionSet }; int result = performDexOptInternalWithDependenciesLI(p, instructionSets, checkProfiles, targetCompilerFilter, force); return result == PackageDexOptimizer.DEX_OPT_PERFORMED; return result != PackageDexOptimizer.DEX_OPT_FAILED; } } finally { Binder.restoreCallingIdentity(callingId); Loading