Merge changes If7388e15,Ica73e67a,Ia4d4362e into nyc-dev

* changes:
  Assume package failed to compile unless proven otherwise
  Refactor return values of performDexOpt
  Update packages in post-boot background job
This commit is contained in:
David Brazdil
2016-04-11 11:15:55 +00:00
committed by Android (Google) Code Review
3 changed files with 170 additions and 41 deletions

View File

@@ -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,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)
.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.
sFailedPackageNames.add(pkg);
// 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;
}
}

View File

@@ -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;
@@ -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) {
@@ -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.
return performedDexOpt ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
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;
}
}
/**

View File

@@ -7147,6 +7147,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;
@@ -7167,7 +7169,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);