Merge "Work on issue #28942589: Tune job scheduler" into nyc-dev

This commit is contained in:
Dianne Hackborn
2016-06-01 16:27:04 +00:00
committed by Android (Google) Code Review
15 changed files with 529 additions and 154 deletions

View File

@@ -596,6 +596,22 @@ public class AlarmManager {
null, workSource, null); null, workSource, null);
} }
/**
* Direct callback version of {@link #set(int, long, long, long, PendingIntent, WorkSource)}.
* Note that repeating alarms must use the PendingIntent variant, not an OnAlarmListener.
* <p>
* The OnAlarmListener's {@link OnAlarmListener#onAlarm() onAlarm()} method will be
* invoked via the specified target Handler, or on the application's main looper
* if {@code null} is passed as the {@code targetHandler} parameter.
*
* @hide
*/
public void set(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
String tag, OnAlarmListener listener, Handler targetHandler, WorkSource workSource) {
setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, null, listener, tag,
targetHandler, workSource, null);
}
/** /**
* Direct callback version of {@link #set(int, long, long, long, PendingIntent, WorkSource)}. * Direct callback version of {@link #set(int, long, long, long, PendingIntent, WorkSource)}.
* Note that repeating alarms must use the PendingIntent variant, not an OnAlarmListener. * Note that repeating alarms must use the PendingIntent variant, not an OnAlarmListener.

View File

@@ -192,7 +192,8 @@ public class JobInfo implements Parcelable {
private final int flags; private final int flags;
/** /**
* Unique job id associated with this class. This is assigned to your job by the scheduler. * Unique job id associated with this application (uid). This is the same job ID
* you supplied in the {@link Builder} constructor.
*/ */
public int getId() { public int getId() {
return jobId; return jobId;
@@ -524,9 +525,9 @@ public class JobInfo implements Parcelable {
/** Builder class for constructing {@link JobInfo} objects. */ /** Builder class for constructing {@link JobInfo} objects. */
public static final class Builder { public static final class Builder {
private int mJobId; private final int mJobId;
private final ComponentName mJobService;
private PersistableBundle mExtras = PersistableBundle.EMPTY; private PersistableBundle mExtras = PersistableBundle.EMPTY;
private ComponentName mJobService;
private int mPriority = PRIORITY_DEFAULT; private int mPriority = PRIORITY_DEFAULT;
private int mFlags; private int mFlags;
// Requirements. // Requirements.
@@ -553,9 +554,13 @@ public class JobInfo implements Parcelable {
private boolean mBackoffPolicySet = false; private boolean mBackoffPolicySet = false;
/** /**
* Initialize a new Builder to construct a {@link JobInfo}.
*
* @param jobId Application-provided id for this job. Subsequent calls to cancel, or * @param jobId Application-provided id for this job. Subsequent calls to cancel, or
* jobs created with the same jobId, will update the pre-existing job with * jobs created with the same jobId, will update the pre-existing job with
* the same id. * the same id. This ID must be unique across all clients of the same uid
* (not just the same package). You will want to make sure this is a stable
* id across app updates, so probably not based on a resource ID.
* @param jobService The endpoint that you implement that will receive the callback from the * @param jobService The endpoint that you implement that will receive the callback from the
* JobScheduler. * JobScheduler.
*/ */

View File

@@ -8017,6 +8017,36 @@ public final class Settings {
*/ */
public static final String ALARM_MANAGER_CONSTANTS = "alarm_manager_constants"; public static final String ALARM_MANAGER_CONSTANTS = "alarm_manager_constants";
/**
* Job scheduler specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
*
* "min_ready_jobs_count=2,moderate_use_factor=.5"
*
* The following keys are supported:
*
* <pre>
* min_idle_count (int)
* min_charging_count (int)
* min_connectivity_count (int)
* min_content_count (int)
* min_ready_jobs_count (int)
* heavy_use_factor (float)
* moderate_use_factor (float)
* fg_job_count (int)
* bg_normal_job_count (int)
* bg_moderate_job_count (int)
* bg_low_job_count (int)
* bg_critical_job_count (int)
* </pre>
*
* <p>
* Type: string
* @hide
* @see com.android.server.job.JobSchedulerService.Constants
*/
public static final String JOB_SCHEDULER_CONSTANTS = "job_scheduler_constants";
/** /**
* ShortcutManager specific settings. * ShortcutManager specific settings.
* This is encoded as a key=value list, separated by commas. Ex: * This is encoded as a key=value list, separated by commas. Ex:

View File

@@ -62,6 +62,24 @@ public class KeyValueListParser {
} }
} }
/**
* Get the value for key as an int.
* @param key The key to lookup.
* @param def The value to return if the key was not found, or the value was not a long.
* @return the int value associated with the key.
*/
public int getInt(String key, int def) {
String value = mValues.get(key);
if (value != null) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
// fallthrough
}
}
return def;
}
/** /**
* Get the value for key as a long. * Get the value for key as a long.
* @param key The key to lookup. * @param key The key to lookup.

View File

@@ -105,6 +105,8 @@ public final class JobPackageTracker {
final long mStartElapsedTime; final long mStartElapsedTime;
final long mStartClockTime; final long mStartClockTime;
long mSummedTime; long mSummedTime;
int mMaxTotalActive;
int mMaxFgActive;
public DataSet(DataSet otherTimes) { public DataSet(DataSet otherTimes) {
mStartUptimeTime = otherTimes.mStartUptimeTime; mStartUptimeTime = otherTimes.mStartUptimeTime;
@@ -257,6 +259,12 @@ public final class JobPackageTracker {
} }
} }
} }
if (mMaxTotalActive > out.mMaxTotalActive) {
out.mMaxTotalActive = mMaxTotalActive;
}
if (mMaxFgActive > out.mMaxFgActive) {
out.mMaxFgActive = mMaxFgActive;
}
} }
void printDuration(PrintWriter pw, long period, long duration, int count, String suffix) { void printDuration(PrintWriter pw, long period, long duration, int count, String suffix) {
@@ -317,6 +325,9 @@ public final class JobPackageTracker {
pw.println(); pw.println();
} }
} }
pw.print(prefix); pw.print(" Max concurrency: ");
pw.print(mMaxTotalActive); pw.print(" total, ");
pw.print(mMaxFgActive); pw.println(" foreground");
} }
} }
@@ -366,6 +377,15 @@ public final class JobPackageTracker {
addEvent(EVENT_STOP_JOB, job.getSourceUid(), job.getBatteryName()); addEvent(EVENT_STOP_JOB, job.getSourceUid(), job.getBatteryName());
} }
public void noteConcurrency(int totalActive, int fgActive) {
if (totalActive > mCurDataSet.mMaxTotalActive) {
mCurDataSet.mMaxTotalActive = totalActive;
}
if (fgActive > mCurDataSet.mMaxFgActive) {
mCurDataSet.mMaxFgActive = fgActive;
}
}
public float getLoadFactor(JobStatus job) { public float getLoadFactor(JobStatus job) {
final int uid = job.getSourceUid(); final int uid = job.getSourceUid();
final String pkg = job.getSourcePackageName(); final String pkg = job.getSourcePackageName();

View File

@@ -23,6 +23,8 @@ import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@@ -37,6 +39,7 @@ import android.app.job.JobService;
import android.app.job.IJobScheduler; import android.app.job.IJobScheduler;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
@@ -44,6 +47,7 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.net.Uri; import android.net.Uri;
import android.os.BatteryStats; import android.os.BatteryStats;
import android.os.Binder; import android.os.Binder;
@@ -57,6 +61,8 @@ import android.os.ResultReceiver;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserHandle; import android.os.UserHandle;
import android.provider.Settings;
import android.util.KeyValueListParser;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseIntArray; import android.util.SparseIntArray;
@@ -98,17 +104,12 @@ public final class JobSchedulerService extends com.android.server.SystemService
public static final boolean DEBUG = false; public static final boolean DEBUG = false;
/** The maximum number of concurrent jobs we run at one time. */ /** The maximum number of concurrent jobs we run at one time. */
private static final int MAX_JOB_CONTEXTS_COUNT = 12; private static final int MAX_JOB_CONTEXTS_COUNT = 16;
/** The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app. */
private static final int FG_JOB_CONTEXTS_COUNT = 4;
/** Enforce a per-app limit on scheduled jobs? */ /** Enforce a per-app limit on scheduled jobs? */
private static final boolean ENFORCE_MAX_JOBS = true; private static final boolean ENFORCE_MAX_JOBS = true;
/** The maximum number of jobs that we allow an unprivileged app to schedule */ /** The maximum number of jobs that we allow an unprivileged app to schedule */
private static final int MAX_JOBS_PER_APP = 100; private static final int MAX_JOBS_PER_APP = 100;
/** This is the job execution factor that is considered to be heavy use of the system. */
private static final float HEAVY_USE_FACTOR = .9f;
/** This is the job execution factor that is considered to be moderate use of the system. */
private static final float MODERATE_USE_FACTOR = .5f;
/** Global local for all job scheduler state. */ /** Global local for all job scheduler state. */
final Object mLock = new Object(); final Object mLock = new Object();
@@ -122,34 +123,6 @@ public final class JobSchedulerService extends com.android.server.SystemService
static final int MSG_STOP_JOB = 2; static final int MSG_STOP_JOB = 2;
static final int MSG_CHECK_JOB_GREEDY = 3; static final int MSG_CHECK_JOB_GREEDY = 3;
// Policy constants
/**
* Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
* early.
*/
static final int MIN_IDLE_COUNT = 1;
/**
* Minimum # of charging jobs that must be ready in order to force the JMS to schedule things
* early.
*/
static final int MIN_CHARGING_COUNT = 1;
/**
* Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
* things early.
*/
static final int MIN_CONNECTIVITY_COUNT = 1; // Run connectivity jobs as soon as ready.
/**
* Minimum # of content trigger jobs that must be ready in order to force the JMS to schedule
* things early.
*/
static final int MIN_CONTENT_COUNT = 1;
/**
* Minimum # of jobs (with no particular constraints) for which the JMS will be happy running
* some work early.
* This is correlated with the amount of batching we'll be able to do.
*/
static final int MIN_READY_JOBS_COUNT = 2;
/** /**
* Track Services that have currently active or pending jobs. The index is provided by * Track Services that have currently active or pending jobs. The index is provided by
* {@link JobStatus#getServiceToken()} * {@link JobStatus#getServiceToken()}
@@ -186,7 +159,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
* Current limit on the number of concurrent JobServiceContext entries we want to * Current limit on the number of concurrent JobServiceContext entries we want to
* keep actively running a job. * keep actively running a job.
*/ */
int mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT; int mMaxActiveJobs = 1;
/** /**
* Which uids are currently in the foreground. * Which uids are currently in the foreground.
@@ -211,6 +184,211 @@ public final class JobSchedulerService extends com.android.server.SystemService
*/ */
int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
/**
* All times are in milliseconds. These constants are kept synchronized with the system
* global Settings. Any access to this class or its fields should be done while
* holding the JobSchedulerService.mLock lock.
*/
private final class Constants extends ContentObserver {
// Key names stored in the settings value.
private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
private static final String KEY_FG_JOB_COUNT = "fg_job_count";
private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
private static final int DEFAULT_MIN_IDLE_COUNT = 1;
private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
private static final int DEFAULT_FG_JOB_COUNT = 4;
private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
private static final int DEFAULT_BG_LOW_JOB_COUNT = 2;
private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
/**
* Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
* early.
*/
int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
/**
* Minimum # of charging jobs that must be ready in order to force the JMS to schedule
* things early.
*/
int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
/**
* Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
* things early. 1 == Run connectivity jobs as soon as ready.
*/
int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
/**
* Minimum # of content trigger jobs that must be ready in order to force the JMS to
* schedule things early.
*/
int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
/**
* Minimum # of jobs (with no particular constraints) for which the JMS will be happy
* running some work early. This (and thus the other min counts) is now set to 1, to
* prevent any batching at this level. Since we now do batching through doze, that is
* a much better mechanism.
*/
int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
/**
* This is the job execution factor that is considered to be heavy use of the system.
*/
float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
/**
* This is the job execution factor that is considered to be moderate use of the system.
*/
float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
/**
* The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
*/
int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
/**
* The maximum number of background jobs we allow when the system is in a normal
* memory state.
*/
int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
/**
* The maximum number of background jobs we allow when the system is in a moderate
* memory state.
*/
int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
/**
* The maximum number of background jobs we allow when the system is in a low
* memory state.
*/
int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
/**
* The maximum number of background jobs we allow when the system is in a critical
* memory state.
*/
int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
public Constants(Handler handler) {
super(handler);
}
public void start(ContentResolver resolver) {
mResolver = resolver;
mResolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
updateConstants();
}
@Override
public void onChange(boolean selfChange, Uri uri) {
updateConstants();
}
private void updateConstants() {
synchronized (mLock) {
try {
mParser.setString(Settings.Global.getString(mResolver,
Settings.Global.ALARM_MANAGER_CONSTANTS));
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
Slog.e(TAG, "Bad device idle settings", e);
}
MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
DEFAULT_MIN_IDLE_COUNT);
MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
DEFAULT_MIN_CHARGING_COUNT);
MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
DEFAULT_MIN_CONNECTIVITY_COUNT);
MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
DEFAULT_MIN_CONTENT_COUNT);
MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
DEFAULT_MIN_READY_JOBS_COUNT);
HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
DEFAULT_HEAVY_USE_FACTOR);
MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
DEFAULT_MODERATE_USE_FACTOR);
FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
DEFAULT_FG_JOB_COUNT);
BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
DEFAULT_BG_NORMAL_JOB_COUNT);
if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
}
BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
DEFAULT_BG_MODERATE_JOB_COUNT);
if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
}
BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
DEFAULT_BG_LOW_JOB_COUNT);
if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
}
BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
DEFAULT_BG_CRITICAL_JOB_COUNT);
if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
}
}
}
void dump(PrintWriter pw) {
pw.println(" Settings:");
pw.print(" "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
pw.print(MIN_IDLE_COUNT); pw.println();
pw.print(" "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
pw.print(MIN_CHARGING_COUNT); pw.println();
pw.print(" "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
pw.print(" "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
pw.print(MIN_CONTENT_COUNT); pw.println();
pw.print(" "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
pw.print(MIN_READY_JOBS_COUNT); pw.println();
pw.print(" "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
pw.print(HEAVY_USE_FACTOR); pw.println();
pw.print(" "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
pw.print(MODERATE_USE_FACTOR); pw.println();
pw.print(" "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
pw.print(FG_JOB_COUNT); pw.println();
pw.print(" "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
pw.print(BG_NORMAL_JOB_COUNT); pw.println();
pw.print(" "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
pw.print(BG_MODERATE_JOB_COUNT); pw.println();
pw.print(" "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
pw.print(BG_LOW_JOB_COUNT); pw.println();
pw.print(" "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
}
}
final Constants mConstants;
/** /**
* Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
* still clean up. On reinstall the package will have a new uid. * still clean up. On reinstall the package will have a new uid.
@@ -550,6 +728,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
mControllers.add(DeviceIdleJobsController.get(this)); mControllers.add(DeviceIdleJobsController.get(this));
mHandler = new JobHandler(context.getMainLooper()); mHandler = new JobHandler(context.getMainLooper());
mConstants = new Constants(mHandler);
mJobSchedulerStub = new JobSchedulerStub(); mJobSchedulerStub = new JobSchedulerStub();
mJobs = JobStore.initAndGet(this); mJobs = JobStore.initAndGet(this);
} }
@@ -563,6 +742,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
@Override @Override
public void onBootPhase(int phase) { public void onBootPhase(int phase) {
if (PHASE_SYSTEM_SERVICES_READY == phase) { if (PHASE_SYSTEM_SERVICES_READY == phase) {
mConstants.start(getContext().getContentResolver());
// Register br for package removals and user removals. // Register br for package removals and user removals.
final IntentFilter filter = new IntentFilter(); final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -994,11 +1174,12 @@ public final class JobSchedulerService extends com.android.server.SystemService
public void postProcess() { public void postProcess() {
if (backoffCount > 0 || if (backoffCount > 0 ||
idleCount >= MIN_IDLE_COUNT || idleCount >= mConstants.MIN_IDLE_COUNT ||
connectivityCount >= MIN_CONNECTIVITY_COUNT || connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
chargingCount >= MIN_CHARGING_COUNT || chargingCount >= mConstants.MIN_CHARGING_COUNT ||
contentCount >= MIN_CONTENT_COUNT || contentCount >= mConstants.MIN_CONTENT_COUNT ||
(runnableJobs != null && runnableJobs.size() >= MIN_READY_JOBS_COUNT)) { (runnableJobs != null
&& runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs."); Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
} }
@@ -1095,9 +1276,9 @@ public final class JobSchedulerService extends com.android.server.SystemService
private int adjustJobPriority(int curPriority, JobStatus job) { private int adjustJobPriority(int curPriority, JobStatus job) {
if (curPriority < JobInfo.PRIORITY_TOP_APP) { if (curPriority < JobInfo.PRIORITY_TOP_APP) {
float factor = mJobPackageTracker.getLoadFactor(job); float factor = mJobPackageTracker.getLoadFactor(job);
if (factor >= HEAVY_USE_FACTOR) { if (factor >= mConstants.HEAVY_USE_FACTOR) {
curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING; curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
} else if (factor >= MODERATE_USE_FACTOR) { } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING; curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
} }
} }
@@ -1135,16 +1316,16 @@ public final class JobSchedulerService extends com.android.server.SystemService
} }
switch (memLevel) { switch (memLevel) {
case ProcessStats.ADJ_MEM_FACTOR_MODERATE: case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
mMaxActiveJobs = ((MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT) * 2) / 3; mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
break; break;
case ProcessStats.ADJ_MEM_FACTOR_LOW: case ProcessStats.ADJ_MEM_FACTOR_LOW:
mMaxActiveJobs = (MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT) / 3; mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
break; break;
case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
mMaxActiveJobs = 1; mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
break; break;
default: default:
mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT; mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
break; break;
} }
@@ -1152,10 +1333,15 @@ public final class JobSchedulerService extends com.android.server.SystemService
boolean[] act = mTmpAssignAct; boolean[] act = mTmpAssignAct;
int[] preferredUidForContext = mTmpAssignPreferredUidForContext; int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
int numActive = 0; int numActive = 0;
int numForeground = 0;
for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
final JobServiceContext js = mActiveServices.get(i); final JobServiceContext js = mActiveServices.get(i);
if ((contextIdToJobMap[i] = js.getRunningJob()) != null) { final JobStatus status = js.getRunningJob();
if ((contextIdToJobMap[i] = status) != null) {
numActive++; numActive++;
if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
numForeground++;
}
} }
act[i] = false; act[i] = false;
preferredUidForContext[i] = js.getPreferredUid(); preferredUidForContext[i] = js.getPreferredUid();
@@ -1184,13 +1370,14 @@ public final class JobSchedulerService extends com.android.server.SystemService
JobStatus job = contextIdToJobMap[j]; JobStatus job = contextIdToJobMap[j];
int preferredUid = preferredUidForContext[j]; int preferredUid = preferredUidForContext[j];
if (job == null) { if (job == null) {
if ((numActive < mMaxActiveJobs || priority >= JobInfo.PRIORITY_TOP_APP) && if ((numActive < mMaxActiveJobs ||
(priority >= JobInfo.PRIORITY_TOP_APP &&
numForeground < mConstants.FG_JOB_COUNT)) &&
(preferredUid == nextPending.getUid() || (preferredUid == nextPending.getUid() ||
preferredUid == JobServiceContext.NO_PREFERRED_UID)) { preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
// This slot is free, and we haven't yet hit the limit on // This slot is free, and we haven't yet hit the limit on
// concurrent jobs... we can just throw the job in to here. // concurrent jobs... we can just throw the job in to here.
minPriorityContextId = j; minPriorityContextId = j;
numActive++;
break; break;
} }
// No job on this context, but nextPending can't run here because // No job on this context, but nextPending can't run here because
@@ -1212,11 +1399,16 @@ public final class JobSchedulerService extends com.android.server.SystemService
if (minPriorityContextId != -1) { if (minPriorityContextId != -1) {
contextIdToJobMap[minPriorityContextId] = nextPending; contextIdToJobMap[minPriorityContextId] = nextPending;
act[minPriorityContextId] = true; act[minPriorityContextId] = true;
numActive++;
if (priority >= JobInfo.PRIORITY_TOP_APP) {
numForeground++;
}
} }
} }
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
} }
mJobPackageTracker.noteConcurrency(numActive, numForeground);
for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
boolean preservePreferredUid = false; boolean preservePreferredUid = false;
if (act[i]) { if (act[i]) {
@@ -1569,20 +1761,34 @@ public final class JobSchedulerService extends com.android.server.SystemService
final int filterUidFinal = UserHandle.getAppId(filterUid); final int filterUidFinal = UserHandle.getAppId(filterUid);
final long now = SystemClock.elapsedRealtime(); final long now = SystemClock.elapsedRealtime();
synchronized (mLock) { synchronized (mLock) {
mConstants.dump(pw);
pw.println();
pw.println("Started users: " + Arrays.toString(mStartedUsers)); pw.println("Started users: " + Arrays.toString(mStartedUsers));
pw.println("Registered jobs:"); pw.print("Registered ");
pw.print(mJobs.size());
pw.println(" jobs:");
if (mJobs.size() > 0) { if (mJobs.size() > 0) {
mJobs.forEachJob(new JobStatusFunctor() { final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
private int index = 0; Collections.sort(jobs, new Comparator<JobStatus>() {
@Override @Override
public void process(JobStatus job) { public int compare(JobStatus o1, JobStatus o2) {
pw.print(" Job #"); pw.print(index++); pw.print(": "); int uid1 = o1.getUid();
pw.println(job.toShortString()); int uid2 = o2.getUid();
int id1 = o1.getJobId();
int id2 = o2.getJobId();
if (uid1 != uid2) {
return uid1 < uid2 ? -1 : 1;
}
return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
}
});
for (JobStatus job : jobs) {
pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
pw.println(job.toShortStringExceptUniqueId());
// Skip printing details if the caller requested a filter // Skip printing details if the caller requested a filter
if (!job.shouldDump(filterUidFinal)) { if (!job.shouldDump(filterUidFinal)) {
return; continue;
} }
job.dump(pw, " ", true); job.dump(pw, " ", true);
@@ -1598,7 +1804,6 @@ public final class JobSchedulerService extends com.android.server.SystemService
pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId())); pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
pw.println(")"); pw.println(")");
} }
});
} else { } else {
pw.println(" None."); pw.println(" None.");
} }

View File

@@ -844,8 +844,16 @@ public class JobStore {
// Inefficient; use only for testing // Inefficient; use only for testing
public List<JobStatus> getAllJobs() { public List<JobStatus> getAllJobs() {
ArrayList<JobStatus> allJobs = new ArrayList<JobStatus>(size()); ArrayList<JobStatus> allJobs = new ArrayList<JobStatus>(size());
for (int i = mJobs.size(); i >= 0; i--) { for (int i = mJobs.size() - 1; i >= 0; i--) {
allJobs.addAll(mJobs.valueAt(i)); ArraySet<JobStatus> jobs = mJobs.valueAt(i);
if (jobs != null) {
// Use a for loop over the ArraySet, so we don't need to make its
// optional collection class iterator implementation or have to go
// through a temporary array from toArray().
for (int j = jobs.size() - 1; j >= 0; j--) {
allJobs.add(jobs.valueAt(j));
}
}
} }
return allJobs; return allJobs;
} }

View File

@@ -18,6 +18,7 @@ package com.android.server.job.controllers;
import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal;
import android.content.Context; import android.content.Context;
import android.os.UserHandle;
import android.util.Slog; import android.util.Slog;
import com.android.server.LocalServices; import com.android.server.LocalServices;
@@ -42,6 +43,7 @@ public class AppIdleController extends StateController {
private static volatile AppIdleController sController; private static volatile AppIdleController sController;
private final JobSchedulerService mJobSchedulerService; private final JobSchedulerService mJobSchedulerService;
private final UsageStatsManagerInternal mUsageStatsInternal; private final UsageStatsManagerInternal mUsageStatsInternal;
private boolean mInitializedParoleOn;
boolean mAppIdleParoleOn; boolean mAppIdleParoleOn;
final class GlobalUpdateFunc implements JobStore.JobStatusFunctor { final class GlobalUpdateFunc implements JobStore.JobStatusFunctor {
@@ -100,12 +102,16 @@ public class AppIdleController extends StateController {
super(service, context, lock); super(service, context, lock);
mJobSchedulerService = service; mJobSchedulerService = service;
mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class); mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn(); mAppIdleParoleOn = true;
mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener()); mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
} }
@Override @Override
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
if (!mInitializedParoleOn) {
mInitializedParoleOn = true;
mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn();
}
String packageName = jobStatus.getSourcePackageName(); String packageName = jobStatus.getSourcePackageName();
final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName, final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
jobStatus.getSourceUid(), jobStatus.getSourceUserId()); jobStatus.getSourceUid(), jobStatus.getSourceUserId());
@@ -122,21 +128,24 @@ public class AppIdleController extends StateController {
@Override @Override
public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) { public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) {
pw.println("AppIdle"); pw.print("AppIdle: parole on = ");
pw.println("Parole On: " + mAppIdleParoleOn); pw.println(mAppIdleParoleOn);
mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() { mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
@Override public void process(JobStatus jobStatus) { @Override public void process(JobStatus jobStatus) {
// Skip printing details if the caller requested a filter // Skip printing details if the caller requested a filter
if (!jobStatus.shouldDump(filterUid)) { if (!jobStatus.shouldDump(filterUid)) {
return; return;
} }
pw.print(" "); pw.print(" #");
jobStatus.printUniqueId(pw);
pw.print(" from ");
UserHandle.formatUid(pw, jobStatus.getSourceUid());
pw.print(": ");
pw.print(jobStatus.getSourcePackageName()); pw.print(jobStatus.getSourcePackageName());
pw.print(": runnable="); pw.print(", runnable=");
pw.println((jobStatus.satisfiedConstraints&JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0); pw.println((jobStatus.satisfiedConstraints&JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0);
} }
}); });
pw.println();
} }
void setAppIdleParoleOn(boolean isAppIdleParoleOn) { void setAppIdleParoleOn(boolean isAppIdleParoleOn) {

View File

@@ -23,6 +23,7 @@ import android.content.IntentFilter;
import android.os.BatteryManager; import android.os.BatteryManager;
import android.os.BatteryManagerInternal; import android.os.BatteryManagerInternal;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog; import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
@@ -195,21 +196,21 @@ public class BatteryController extends StateController {
@Override @Override
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
pw.println("Batt."); pw.print("Battery: stable power = ");
pw.println("Stable power: " + mChargeTracker.isOnStablePower()); pw.println(mChargeTracker.isOnStablePower());
Iterator<JobStatus> it = mTrackedTasks.iterator(); pw.print("Tracking ");
if (it.hasNext()) { pw.print(mTrackedTasks.size());
JobStatus jobStatus = it.next(); pw.println(":");
if (jobStatus.shouldDump(filterUid)) { for (int i = 0; i < mTrackedTasks.size(); i++) {
pw.print(String.valueOf(jobStatus.hashCode())); final JobStatus js = mTrackedTasks.get(i);
} if (!js.shouldDump(filterUid)) {
} continue;
while (it.hasNext()) {
JobStatus jobStatus = it.next();
if (jobStatus.shouldDump(filterUid)) {
pw.print("," + String.valueOf(jobStatus.hashCode()));
}
} }
pw.print(" #");
js.printUniqueId(pw);
pw.print(" from ");
UserHandle.formatUid(pw, js.getSourceUid());
pw.println(); pw.println();
} }
}
} }

View File

@@ -187,14 +187,20 @@ public class ConnectivityController extends StateController implements
@Override @Override
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
pw.println("Conn."); pw.println("Connectivity.");
pw.print("Tracking ");
pw.print(mTrackedJobs.size());
pw.println(":");
for (int i = 0; i < mTrackedJobs.size(); i++) { for (int i = 0; i < mTrackedJobs.size(); i++) {
final JobStatus js = mTrackedJobs.get(i); final JobStatus js = mTrackedJobs.get(i);
if (js.shouldDump(filterUid)) { if (js.shouldDump(filterUid)) {
pw.println(String.valueOf(js.getJobId() + "," + js.getUid()) pw.print(" #");
+ ": C=" + js.hasConnectivityConstraint() js.printUniqueId(pw);
+ ", UM=" + js.hasUnmeteredConstraint() pw.print(" from ");
+ ", NR=" + js.hasNotRoamingConstraint()); UserHandle.formatUid(pw, js.getSourceUid());
pw.print(": C="); pw.print(js.hasConnectivityConstraint());
pw.print(": UM="); pw.print(js.hasUnmeteredConstraint());
pw.print(": NR="); pw.println(js.hasNotRoamingConstraint());
} }
} }
} }

View File

@@ -21,6 +21,7 @@ import android.content.Context;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.net.Uri; import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.UserHandle;
import android.util.TimeUtils; import android.util.TimeUtils;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
@@ -323,23 +324,17 @@ public class ContentObserverController extends StateController {
@Override @Override
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
pw.println("Content."); pw.println("Content:");
boolean printed = false;
Iterator<JobStatus> it = mTrackedTasks.iterator(); Iterator<JobStatus> it = mTrackedTasks.iterator();
while (it.hasNext()) { while (it.hasNext()) {
JobStatus js = it.next(); JobStatus js = it.next();
if (!js.shouldDump(filterUid)) { if (!js.shouldDump(filterUid)) {
continue; continue;
} }
if (!printed) { pw.print(" #");
pw.print(" "); js.printUniqueId(pw);
printed = true; pw.print(" from ");
} else { UserHandle.formatUid(pw, js.getSourceUid());
pw.print(",");
}
pw.print(System.identityHashCode(js));
}
if (printed) {
pw.println(); pw.println();
} }
int N = mObservers.size(); int N = mObservers.size();
@@ -367,8 +362,10 @@ public class ContentObserverController extends StateController {
pw.println(" Jobs:"); pw.println(" Jobs:");
for (int j=0; j<M; j++) { for (int j=0; j<M; j++) {
JobInstance inst = obs.mJobs.valueAt(j); JobInstance inst = obs.mJobs.valueAt(j);
pw.print(" "); pw.print(" #");
pw.print(System.identityHashCode(inst.mJobStatus)); inst.mJobStatus.printUniqueId(pw);
pw.print(" from ");
UserHandle.formatUid(pw, inst.mJobStatus.getSourceUid());
if (inst.mChangedAuthorities != null) { if (inst.mChangedAuthorities != null) {
pw.println(":"); pw.println(":");
if (inst.mTriggerPending) { if (inst.mTriggerPending) {

View File

@@ -180,13 +180,16 @@ public class DeviceIdleJobsController extends StateController {
if (!jobStatus.shouldDump(filterUid)) { if (!jobStatus.shouldDump(filterUid)) {
return; return;
} }
pw.print(" "); pw.print(" #");
jobStatus.printUniqueId(pw);
pw.print(" from ");
UserHandle.formatUid(pw, jobStatus.getSourceUid());
pw.print(": ");
pw.print(jobStatus.getSourcePackageName()); pw.print(jobStatus.getSourcePackageName());
pw.print(": runnable="); pw.print(", runnable=");
pw.println((jobStatus.satisfiedConstraints pw.println((jobStatus.satisfiedConstraints
& JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0); & JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0);
} }
}); });
pw.println();
} }
} }

View File

@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog; import android.util.Slog;
import com.android.server.am.ActivityManagerService; import com.android.server.am.ActivityManagerService;
@@ -191,15 +192,19 @@ public class IdleController extends StateController {
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
pw.print("Idle: "); pw.print("Idle: ");
pw.println(mIdleTracker.isIdle() ? "true" : "false"); pw.println(mIdleTracker.isIdle() ? "true" : "false");
pw.println(mTrackedTasks.size()); pw.print("Tracking ");
pw.print(mTrackedTasks.size());
pw.println(":");
for (int i = 0; i < mTrackedTasks.size(); i++) { for (int i = 0; i < mTrackedTasks.size(); i++) {
final JobStatus js = mTrackedTasks.get(i); final JobStatus js = mTrackedTasks.get(i);
if (!js.shouldDump(filterUid)) { if (!js.shouldDump(filterUid)) {
continue; continue;
} }
pw.print(" "); pw.print(" #");
pw.print(String.valueOf(js.getJobId() + "," + js.getUid())); js.printUniqueId(pw);
pw.println(".."); pw.print(" from ");
UserHandle.formatUid(pw, js.getSourceUid());
pw.println();
} }
} }
} }

View File

@@ -249,6 +249,12 @@ public final class JobStatus {
return job.getId(); return job.getId();
} }
public void printUniqueId(PrintWriter pw) {
UserHandle.formatUid(pw, callingUid);
pw.print("/");
pw.print(job.getId());
}
public int getNumFailures() { public int getNumFailures() {
return numFailures; return numFailures;
} }
@@ -410,6 +416,10 @@ public final class JobStatus {
return true; return true;
} }
boolean isConstraintSatisfied(int constraint) {
return (satisfiedConstraints&constraint) != 0;
}
public boolean shouldDump(int filterUid) { public boolean shouldDump(int filterUid) {
return filterUid == -1 || UserHandle.getAppId(getUid()) == filterUid return filterUid == -1 || UserHandle.getAppId(getUid()) == filterUid
|| UserHandle.getAppId(getSourceUid()) == filterUid; || UserHandle.getAppId(getSourceUid()) == filterUid;
@@ -505,10 +515,22 @@ public final class JobStatus {
public String toShortString() { public String toShortString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" jId="); sb.append(" #");
UserHandle.formatUid(sb, callingUid);
sb.append("/");
sb.append(job.getId()); sb.append(job.getId());
sb.append(' '); sb.append(' ');
UserHandle.formatUid(sb, callingUid); sb.append(batteryName);
return sb.toString();
}
/**
* Convenience function to identify a job uniquely without pulling all the data that
* {@link #toString()} returns.
*/
public String toShortStringExceptUniqueId() {
StringBuilder sb = new StringBuilder();
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(' '); sb.append(' ');
sb.append(batteryName); sb.append(batteryName);
return sb.toString(); return sb.toString();

View File

@@ -20,7 +20,10 @@ import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener; import android.app.AlarmManager.OnAlarmListener;
import android.content.Context; import android.content.Context;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.util.Slog; import android.util.Slog;
import android.util.TimeUtils;
import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateChangedListener; import com.android.server.job.StateChangedListener;
@@ -39,9 +42,9 @@ public class TimeController extends StateController {
private static final String TAG = "JobScheduler.Time"; private static final String TAG = "JobScheduler.Time";
/** Deadline alarm tag for logging purposes */ /** Deadline alarm tag for logging purposes */
private final String DEADLINE_TAG = "JobScheduler.deadline"; private final String DEADLINE_TAG = "*job.deadline*";
/** Delay alarm tag for logging purposes */ /** Delay alarm tag for logging purposes */
private final String DELAY_TAG = "JobScheduler.delay"; private final String DELAY_TAG = "*job.delay*";
private long mNextJobExpiredElapsedMillis; private long mNextJobExpiredElapsedMillis;
private long mNextDelayExpiredElapsedMillis; private long mNextDelayExpiredElapsedMillis;
@@ -91,7 +94,8 @@ public class TimeController extends StateController {
it.add(job); it.add(job);
maybeUpdateAlarmsLocked( maybeUpdateAlarmsLocked(
job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE, job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE,
job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE); job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE,
job.getSourceUid());
} }
} }
@@ -134,6 +138,7 @@ public class TimeController extends StateController {
private void checkExpiredDeadlinesAndResetAlarm() { private void checkExpiredDeadlinesAndResetAlarm() {
synchronized (mLock) { synchronized (mLock) {
long nextExpiryTime = Long.MAX_VALUE; long nextExpiryTime = Long.MAX_VALUE;
int nextExpiryUid = 0;
final long nowElapsedMillis = SystemClock.elapsedRealtime(); final long nowElapsedMillis = SystemClock.elapsedRealtime();
Iterator<JobStatus> it = mTrackedJobs.iterator(); Iterator<JobStatus> it = mTrackedJobs.iterator();
@@ -153,10 +158,11 @@ public class TimeController extends StateController {
it.remove(); it.remove();
} else { // Sorted by expiry time, so take the next one and stop. } else { // Sorted by expiry time, so take the next one and stop.
nextExpiryTime = jobDeadline; nextExpiryTime = jobDeadline;
nextExpiryUid = job.getSourceUid();
break; break;
} }
} }
setDeadlineExpiredAlarmLocked(nextExpiryTime); setDeadlineExpiredAlarmLocked(nextExpiryTime, nextExpiryUid);
} }
} }
@@ -168,6 +174,7 @@ public class TimeController extends StateController {
synchronized (mLock) { synchronized (mLock) {
final long nowElapsedMillis = SystemClock.elapsedRealtime(); final long nowElapsedMillis = SystemClock.elapsedRealtime();
long nextDelayTime = Long.MAX_VALUE; long nextDelayTime = Long.MAX_VALUE;
int nextDelayUid = 0;
boolean ready = false; boolean ready = false;
Iterator<JobStatus> it = mTrackedJobs.iterator(); Iterator<JobStatus> it = mTrackedJobs.iterator();
while (it.hasNext()) { while (it.hasNext()) {
@@ -184,25 +191,29 @@ public class TimeController extends StateController {
if (job.isReady()) { if (job.isReady()) {
ready = true; ready = true;
} }
} else { // Keep going through list to get next delay time. } else if (!job.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY)) {
// If this job still doesn't have its delay constraint satisfied,
// then see if it is the next upcoming delay time for the alarm.
if (nextDelayTime > jobDelayTime) { if (nextDelayTime > jobDelayTime) {
nextDelayTime = jobDelayTime; nextDelayTime = jobDelayTime;
nextDelayUid = job.getSourceUid();
} }
} }
} }
if (ready) { if (ready) {
mStateChangedListener.onControllerStateChanged(); mStateChangedListener.onControllerStateChanged();
} }
setDelayExpiredAlarmLocked(nextDelayTime); setDelayExpiredAlarmLocked(nextDelayTime, nextDelayUid);
} }
} }
private void maybeUpdateAlarmsLocked(long delayExpiredElapsed, long deadlineExpiredElapsed) { private void maybeUpdateAlarmsLocked(long delayExpiredElapsed, long deadlineExpiredElapsed,
int uid) {
if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) { if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) {
setDelayExpiredAlarmLocked(delayExpiredElapsed); setDelayExpiredAlarmLocked(delayExpiredElapsed, uid);
} }
if (deadlineExpiredElapsed < mNextJobExpiredElapsedMillis) { if (deadlineExpiredElapsed < mNextJobExpiredElapsedMillis) {
setDeadlineExpiredAlarmLocked(deadlineExpiredElapsed); setDeadlineExpiredAlarmLocked(deadlineExpiredElapsed, uid);
} }
} }
@@ -211,11 +222,11 @@ public class TimeController extends StateController {
* delay will expire. * delay will expire.
* This alarm <b>will</b> wake up the phone. * This alarm <b>will</b> wake up the phone.
*/ */
private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis) { private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, int uid) {
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis); alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis; mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
updateAlarmWithListenerLocked(DELAY_TAG, mNextDelayExpiredListener, updateAlarmWithListenerLocked(DELAY_TAG, mNextDelayExpiredListener,
mNextDelayExpiredElapsedMillis); mNextDelayExpiredElapsedMillis, uid);
} }
/** /**
@@ -223,11 +234,11 @@ public class TimeController extends StateController {
* deadline will expire. * deadline will expire.
* This alarm <b>will</b> wake up the phone. * This alarm <b>will</b> wake up the phone.
*/ */
private void setDeadlineExpiredAlarmLocked(long alarmTimeElapsedMillis) { private void setDeadlineExpiredAlarmLocked(long alarmTimeElapsedMillis, int uid) {
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis); alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis; mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis;
updateAlarmWithListenerLocked(DEADLINE_TAG, mDeadlineExpiredListener, updateAlarmWithListenerLocked(DEADLINE_TAG, mDeadlineExpiredListener,
mNextJobExpiredElapsedMillis); mNextJobExpiredElapsedMillis, uid);
} }
private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) { private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) {
@@ -239,7 +250,7 @@ public class TimeController extends StateController {
} }
private void updateAlarmWithListenerLocked(String tag, OnAlarmListener listener, private void updateAlarmWithListenerLocked(String tag, OnAlarmListener listener,
long alarmTimeElapsed) { long alarmTimeElapsed, int uid) {
ensureAlarmServiceLocked(); ensureAlarmServiceLocked();
if (alarmTimeElapsed == Long.MAX_VALUE) { if (alarmTimeElapsed == Long.MAX_VALUE) {
mAlarmService.cancel(listener); mAlarmService.cancel(listener);
@@ -248,7 +259,7 @@ public class TimeController extends StateController {
Slog.d(TAG, "Setting " + tag + " for: " + alarmTimeElapsed); Slog.d(TAG, "Setting " + tag + " for: " + alarmTimeElapsed);
} }
mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTimeElapsed, mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTimeElapsed,
tag, listener, null); AlarmManager.WINDOW_HEURISTIC, 0, tag, listener, null, new WorkSource(uid));
} }
} }
@@ -277,20 +288,39 @@ public class TimeController extends StateController {
@Override @Override
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
final long nowElapsed = SystemClock.elapsedRealtime(); final long nowElapsed = SystemClock.elapsedRealtime();
pw.println("Alarms (" + SystemClock.elapsedRealtime() + ")"); pw.print("Alarms: now=");
pw.println( pw.print(SystemClock.elapsedRealtime());
"Next delay alarm in " + (mNextDelayExpiredElapsedMillis - nowElapsed)/1000 + "s"); pw.println();
pw.println("Next deadline alarm in " + (mNextJobExpiredElapsedMillis - nowElapsed)/1000 pw.print("Next delay alarm in ");
+ "s"); TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw);
pw.println("Tracking:"); pw.println();
pw.print("Next deadline alarm in ");
TimeUtils.formatDuration(mNextJobExpiredElapsedMillis, nowElapsed, pw);
pw.println();
pw.print("Tracking ");
pw.print(mTrackedJobs.size());
pw.println(":");
for (JobStatus ts : mTrackedJobs) { for (JobStatus ts : mTrackedJobs) {
if (!ts.shouldDump(filterUid)) { if (!ts.shouldDump(filterUid)) {
continue; continue;
} }
pw.println(String.valueOf(ts.getJobId() + "," + ts.getUid()) pw.print(" #");
+ ": (" + (ts.hasTimingDelayConstraint() ? ts.getEarliestRunTime() : "N/A") ts.printUniqueId(pw);
+ ", " + (ts.hasDeadlineConstraint() ?ts.getLatestRunTimeElapsed() : "N/A") pw.print(" from ");
+ ")"); UserHandle.formatUid(pw, ts.getSourceUid());
pw.print(": Delay=");
if (ts.hasTimingDelayConstraint()) {
TimeUtils.formatDuration(ts.getEarliestRunTime(), nowElapsed, pw);
} else {
pw.print("N/A");
}
pw.print(", Deadline=");
if (ts.hasDeadlineConstraint()) {
TimeUtils.formatDuration(ts.getLatestRunTimeElapsed(), nowElapsed, pw);
} else {
pw.print("N/A");
}
pw.println();
} }
} }
} }