Merge "Work on issue #28942589: Tune job scheduler" into nyc-dev
am: 438693dd6c
* commit '438693dd6cc4e3fe2ffb336d551c0d69e9546102':
Work on issue #28942589: Tune job scheduler
Change-Id: I9e4ddcc64d84a8d943128d8bd8ebf158743c5549
This commit is contained in:
@@ -596,6 +596,22 @@ public class AlarmManager {
|
||||
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)}.
|
||||
* Note that repeating alarms must use the PendingIntent variant, not an OnAlarmListener.
|
||||
|
||||
@@ -192,7 +192,8 @@ public class JobInfo implements Parcelable {
|
||||
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() {
|
||||
return jobId;
|
||||
@@ -524,9 +525,9 @@ public class JobInfo implements Parcelable {
|
||||
|
||||
/** Builder class for constructing {@link JobInfo} objects. */
|
||||
public static final class Builder {
|
||||
private int mJobId;
|
||||
private final int mJobId;
|
||||
private final ComponentName mJobService;
|
||||
private PersistableBundle mExtras = PersistableBundle.EMPTY;
|
||||
private ComponentName mJobService;
|
||||
private int mPriority = PRIORITY_DEFAULT;
|
||||
private int mFlags;
|
||||
// Requirements.
|
||||
@@ -553,11 +554,15 @@ public class JobInfo implements Parcelable {
|
||||
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
|
||||
* jobs created with the same jobId, will update the pre-existing job with
|
||||
* the same id.
|
||||
* jobs created with the same jobId, will update the pre-existing job with
|
||||
* 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
|
||||
* JobScheduler.
|
||||
* JobScheduler.
|
||||
*/
|
||||
public Builder(int jobId, ComponentName jobService) {
|
||||
mJobService = jobService;
|
||||
|
||||
@@ -8017,6 +8017,36 @@ public final class Settings {
|
||||
*/
|
||||
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.
|
||||
* This is encoded as a key=value list, separated by commas. Ex:
|
||||
|
||||
@@ -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.
|
||||
* @param key The key to lookup.
|
||||
|
||||
@@ -105,6 +105,8 @@ public final class JobPackageTracker {
|
||||
final long mStartElapsedTime;
|
||||
final long mStartClockTime;
|
||||
long mSummedTime;
|
||||
int mMaxTotalActive;
|
||||
int mMaxFgActive;
|
||||
|
||||
public DataSet(DataSet otherTimes) {
|
||||
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) {
|
||||
@@ -317,6 +325,9 @@ public final class JobPackageTracker {
|
||||
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());
|
||||
}
|
||||
|
||||
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) {
|
||||
final int uid = job.getSourceUid();
|
||||
final String pkg = job.getSourcePackageName();
|
||||
|
||||
@@ -23,6 +23,8 @@ import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@@ -37,6 +39,7 @@ import android.app.job.JobService;
|
||||
import android.app.job.IJobScheduler;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
@@ -44,6 +47,7 @@ import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.BatteryStats;
|
||||
import android.os.Binder;
|
||||
@@ -57,6 +61,8 @@ import android.os.ResultReceiver;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.KeyValueListParser;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
@@ -98,17 +104,12 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
public static final boolean DEBUG = false;
|
||||
|
||||
/** The maximum number of concurrent jobs we run at one time. */
|
||||
private static final int MAX_JOB_CONTEXTS_COUNT = 12;
|
||||
/** The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app. */
|
||||
private static final int FG_JOB_CONTEXTS_COUNT = 4;
|
||||
private static final int MAX_JOB_CONTEXTS_COUNT = 16;
|
||||
/** Enforce a per-app limit on scheduled jobs? */
|
||||
private static final boolean ENFORCE_MAX_JOBS = true;
|
||||
/** The maximum number of jobs that we allow an unprivileged app to schedule */
|
||||
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. */
|
||||
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_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
|
||||
* {@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
|
||||
* 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.
|
||||
@@ -211,6 +184,211 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
*/
|
||||
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
|
||||
* 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));
|
||||
|
||||
mHandler = new JobHandler(context.getMainLooper());
|
||||
mConstants = new Constants(mHandler);
|
||||
mJobSchedulerStub = new JobSchedulerStub();
|
||||
mJobs = JobStore.initAndGet(this);
|
||||
}
|
||||
@@ -563,6 +742,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
@Override
|
||||
public void onBootPhase(int phase) {
|
||||
if (PHASE_SYSTEM_SERVICES_READY == phase) {
|
||||
mConstants.start(getContext().getContentResolver());
|
||||
// Register br for package removals and user removals.
|
||||
final IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
@@ -994,11 +1174,12 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
|
||||
public void postProcess() {
|
||||
if (backoffCount > 0 ||
|
||||
idleCount >= MIN_IDLE_COUNT ||
|
||||
connectivityCount >= MIN_CONNECTIVITY_COUNT ||
|
||||
chargingCount >= MIN_CHARGING_COUNT ||
|
||||
contentCount >= MIN_CONTENT_COUNT ||
|
||||
(runnableJobs != null && runnableJobs.size() >= MIN_READY_JOBS_COUNT)) {
|
||||
idleCount >= mConstants.MIN_IDLE_COUNT ||
|
||||
connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
|
||||
chargingCount >= mConstants.MIN_CHARGING_COUNT ||
|
||||
contentCount >= mConstants.MIN_CONTENT_COUNT ||
|
||||
(runnableJobs != null
|
||||
&& runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
|
||||
if (DEBUG) {
|
||||
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) {
|
||||
if (curPriority < JobInfo.PRIORITY_TOP_APP) {
|
||||
float factor = mJobPackageTracker.getLoadFactor(job);
|
||||
if (factor >= HEAVY_USE_FACTOR) {
|
||||
if (factor >= mConstants.HEAVY_USE_FACTOR) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1135,16 +1316,16 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
}
|
||||
switch (memLevel) {
|
||||
case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
|
||||
mMaxActiveJobs = ((MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT) * 2) / 3;
|
||||
mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
|
||||
break;
|
||||
case ProcessStats.ADJ_MEM_FACTOR_LOW:
|
||||
mMaxActiveJobs = (MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT) / 3;
|
||||
mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
|
||||
break;
|
||||
case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
|
||||
mMaxActiveJobs = 1;
|
||||
mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
|
||||
break;
|
||||
default:
|
||||
mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT;
|
||||
mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1152,10 +1333,15 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
boolean[] act = mTmpAssignAct;
|
||||
int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
|
||||
int numActive = 0;
|
||||
int numForeground = 0;
|
||||
for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
|
||||
final JobServiceContext js = mActiveServices.get(i);
|
||||
if ((contextIdToJobMap[i] = js.getRunningJob()) != null) {
|
||||
final JobStatus status = js.getRunningJob();
|
||||
if ((contextIdToJobMap[i] = status) != null) {
|
||||
numActive++;
|
||||
if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
|
||||
numForeground++;
|
||||
}
|
||||
}
|
||||
act[i] = false;
|
||||
preferredUidForContext[i] = js.getPreferredUid();
|
||||
@@ -1184,13 +1370,14 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
JobStatus job = contextIdToJobMap[j];
|
||||
int preferredUid = preferredUidForContext[j];
|
||||
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 == JobServiceContext.NO_PREFERRED_UID)) {
|
||||
// This slot is free, and we haven't yet hit the limit on
|
||||
// concurrent jobs... we can just throw the job in to here.
|
||||
minPriorityContextId = j;
|
||||
numActive++;
|
||||
break;
|
||||
}
|
||||
// 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) {
|
||||
contextIdToJobMap[minPriorityContextId] = nextPending;
|
||||
act[minPriorityContextId] = true;
|
||||
numActive++;
|
||||
if (priority >= JobInfo.PRIORITY_TOP_APP) {
|
||||
numForeground++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
|
||||
}
|
||||
mJobPackageTracker.noteConcurrency(numActive, numForeground);
|
||||
for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
|
||||
boolean preservePreferredUid = false;
|
||||
if (act[i]) {
|
||||
@@ -1569,36 +1761,49 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
final int filterUidFinal = UserHandle.getAppId(filterUid);
|
||||
final long now = SystemClock.elapsedRealtime();
|
||||
synchronized (mLock) {
|
||||
mConstants.dump(pw);
|
||||
pw.println();
|
||||
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) {
|
||||
mJobs.forEachJob(new JobStatusFunctor() {
|
||||
private int index = 0;
|
||||
|
||||
final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
|
||||
Collections.sort(jobs, new Comparator<JobStatus>() {
|
||||
@Override
|
||||
public void process(JobStatus job) {
|
||||
pw.print(" Job #"); pw.print(index++); pw.print(": ");
|
||||
pw.println(job.toShortString());
|
||||
|
||||
// Skip printing details if the caller requested a filter
|
||||
if (!job.shouldDump(filterUidFinal)) {
|
||||
return;
|
||||
public int compare(JobStatus o1, JobStatus o2) {
|
||||
int uid1 = o1.getUid();
|
||||
int uid2 = o2.getUid();
|
||||
int id1 = o1.getJobId();
|
||||
int id2 = o2.getJobId();
|
||||
if (uid1 != uid2) {
|
||||
return uid1 < uid2 ? -1 : 1;
|
||||
}
|
||||
|
||||
job.dump(pw, " ", true);
|
||||
pw.print(" Ready: ");
|
||||
pw.print(mHandler.isReadyToBeExecutedLocked(job));
|
||||
pw.print(" (job=");
|
||||
pw.print(job.isReady());
|
||||
pw.print(" pending=");
|
||||
pw.print(mPendingJobs.contains(job));
|
||||
pw.print(" active=");
|
||||
pw.print(isCurrentlyActiveLocked(job));
|
||||
pw.print(" user=");
|
||||
pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
|
||||
pw.println(")");
|
||||
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
|
||||
if (!job.shouldDump(filterUidFinal)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
job.dump(pw, " ", true);
|
||||
pw.print(" Ready: ");
|
||||
pw.print(mHandler.isReadyToBeExecutedLocked(job));
|
||||
pw.print(" (job=");
|
||||
pw.print(job.isReady());
|
||||
pw.print(" pending=");
|
||||
pw.print(mPendingJobs.contains(job));
|
||||
pw.print(" active=");
|
||||
pw.print(isCurrentlyActiveLocked(job));
|
||||
pw.print(" user=");
|
||||
pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
|
||||
pw.println(")");
|
||||
}
|
||||
} else {
|
||||
pw.println(" None.");
|
||||
}
|
||||
|
||||
@@ -844,8 +844,16 @@ public class JobStore {
|
||||
// Inefficient; use only for testing
|
||||
public List<JobStatus> getAllJobs() {
|
||||
ArrayList<JobStatus> allJobs = new ArrayList<JobStatus>(size());
|
||||
for (int i = mJobs.size(); i >= 0; i--) {
|
||||
allJobs.addAll(mJobs.valueAt(i));
|
||||
for (int i = mJobs.size() - 1; i >= 0; 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;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.job.controllers;
|
||||
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.content.Context;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
@@ -42,6 +43,7 @@ public class AppIdleController extends StateController {
|
||||
private static volatile AppIdleController sController;
|
||||
private final JobSchedulerService mJobSchedulerService;
|
||||
private final UsageStatsManagerInternal mUsageStatsInternal;
|
||||
private boolean mInitializedParoleOn;
|
||||
boolean mAppIdleParoleOn;
|
||||
|
||||
final class GlobalUpdateFunc implements JobStore.JobStatusFunctor {
|
||||
@@ -100,12 +102,16 @@ public class AppIdleController extends StateController {
|
||||
super(service, context, lock);
|
||||
mJobSchedulerService = service;
|
||||
mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
|
||||
mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn();
|
||||
mAppIdleParoleOn = true;
|
||||
mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
|
||||
if (!mInitializedParoleOn) {
|
||||
mInitializedParoleOn = true;
|
||||
mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn();
|
||||
}
|
||||
String packageName = jobStatus.getSourcePackageName();
|
||||
final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
|
||||
jobStatus.getSourceUid(), jobStatus.getSourceUserId());
|
||||
@@ -122,21 +128,24 @@ public class AppIdleController extends StateController {
|
||||
|
||||
@Override
|
||||
public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) {
|
||||
pw.println("AppIdle");
|
||||
pw.println("Parole On: " + mAppIdleParoleOn);
|
||||
pw.print("AppIdle: parole on = ");
|
||||
pw.println(mAppIdleParoleOn);
|
||||
mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
|
||||
@Override public void process(JobStatus jobStatus) {
|
||||
// Skip printing details if the caller requested a filter
|
||||
if (!jobStatus.shouldDump(filterUid)) {
|
||||
return;
|
||||
}
|
||||
pw.print(" ");
|
||||
pw.print(" #");
|
||||
jobStatus.printUniqueId(pw);
|
||||
pw.print(" from ");
|
||||
UserHandle.formatUid(pw, jobStatus.getSourceUid());
|
||||
pw.print(": ");
|
||||
pw.print(jobStatus.getSourcePackageName());
|
||||
pw.print(": runnable=");
|
||||
pw.print(", runnable=");
|
||||
pw.println((jobStatus.satisfiedConstraints&JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0);
|
||||
}
|
||||
});
|
||||
pw.println();
|
||||
}
|
||||
|
||||
void setAppIdleParoleOn(boolean isAppIdleParoleOn) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.content.IntentFilter;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.BatteryManagerInternal;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -195,21 +196,21 @@ public class BatteryController extends StateController {
|
||||
|
||||
@Override
|
||||
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
|
||||
pw.println("Batt.");
|
||||
pw.println("Stable power: " + mChargeTracker.isOnStablePower());
|
||||
Iterator<JobStatus> it = mTrackedTasks.iterator();
|
||||
if (it.hasNext()) {
|
||||
JobStatus jobStatus = it.next();
|
||||
if (jobStatus.shouldDump(filterUid)) {
|
||||
pw.print(String.valueOf(jobStatus.hashCode()));
|
||||
pw.print("Battery: stable power = ");
|
||||
pw.println(mChargeTracker.isOnStablePower());
|
||||
pw.print("Tracking ");
|
||||
pw.print(mTrackedTasks.size());
|
||||
pw.println(":");
|
||||
for (int i = 0; i < mTrackedTasks.size(); i++) {
|
||||
final JobStatus js = mTrackedTasks.get(i);
|
||||
if (!js.shouldDump(filterUid)) {
|
||||
continue;
|
||||
}
|
||||
pw.print(" #");
|
||||
js.printUniqueId(pw);
|
||||
pw.print(" from ");
|
||||
UserHandle.formatUid(pw, js.getSourceUid());
|
||||
pw.println();
|
||||
}
|
||||
while (it.hasNext()) {
|
||||
JobStatus jobStatus = it.next();
|
||||
if (jobStatus.shouldDump(filterUid)) {
|
||||
pw.print("," + String.valueOf(jobStatus.hashCode()));
|
||||
}
|
||||
}
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,14 +188,20 @@ public class ConnectivityController extends StateController implements
|
||||
|
||||
@Override
|
||||
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++) {
|
||||
final JobStatus js = mTrackedJobs.get(i);
|
||||
if (js.shouldDump(filterUid)) {
|
||||
pw.println(String.valueOf(js.getJobId() + "," + js.getUid())
|
||||
+ ": C=" + js.hasConnectivityConstraint()
|
||||
+ ", UM=" + js.hasUnmeteredConstraint()
|
||||
+ ", NR=" + js.hasNotRoamingConstraint());
|
||||
pw.print(" #");
|
||||
js.printUniqueId(pw);
|
||||
pw.print(" from ");
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.util.TimeUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
@@ -323,23 +324,17 @@ public class ContentObserverController extends StateController {
|
||||
|
||||
@Override
|
||||
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
|
||||
pw.println("Content.");
|
||||
boolean printed = false;
|
||||
pw.println("Content:");
|
||||
Iterator<JobStatus> it = mTrackedTasks.iterator();
|
||||
while (it.hasNext()) {
|
||||
JobStatus js = it.next();
|
||||
if (!js.shouldDump(filterUid)) {
|
||||
continue;
|
||||
}
|
||||
if (!printed) {
|
||||
pw.print(" ");
|
||||
printed = true;
|
||||
} else {
|
||||
pw.print(",");
|
||||
}
|
||||
pw.print(System.identityHashCode(js));
|
||||
}
|
||||
if (printed) {
|
||||
pw.print(" #");
|
||||
js.printUniqueId(pw);
|
||||
pw.print(" from ");
|
||||
UserHandle.formatUid(pw, js.getSourceUid());
|
||||
pw.println();
|
||||
}
|
||||
int N = mObservers.size();
|
||||
@@ -367,8 +362,10 @@ public class ContentObserverController extends StateController {
|
||||
pw.println(" Jobs:");
|
||||
for (int j=0; j<M; j++) {
|
||||
JobInstance inst = obs.mJobs.valueAt(j);
|
||||
pw.print(" ");
|
||||
pw.print(System.identityHashCode(inst.mJobStatus));
|
||||
pw.print(" #");
|
||||
inst.mJobStatus.printUniqueId(pw);
|
||||
pw.print(" from ");
|
||||
UserHandle.formatUid(pw, inst.mJobStatus.getSourceUid());
|
||||
if (inst.mChangedAuthorities != null) {
|
||||
pw.println(":");
|
||||
if (inst.mTriggerPending) {
|
||||
|
||||
@@ -180,13 +180,16 @@ public class DeviceIdleJobsController extends StateController {
|
||||
if (!jobStatus.shouldDump(filterUid)) {
|
||||
return;
|
||||
}
|
||||
pw.print(" ");
|
||||
pw.print(" #");
|
||||
jobStatus.printUniqueId(pw);
|
||||
pw.print(" from ");
|
||||
UserHandle.formatUid(pw, jobStatus.getSourceUid());
|
||||
pw.print(": ");
|
||||
pw.print(jobStatus.getSourcePackageName());
|
||||
pw.print(": runnable=");
|
||||
pw.print(", runnable=");
|
||||
pw.println((jobStatus.satisfiedConstraints
|
||||
& JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0);
|
||||
}
|
||||
});
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.am.ActivityManagerService;
|
||||
@@ -191,15 +192,19 @@ public class IdleController extends StateController {
|
||||
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
|
||||
pw.print("Idle: ");
|
||||
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++) {
|
||||
final JobStatus js = mTrackedTasks.get(i);
|
||||
if (!js.shouldDump(filterUid)) {
|
||||
continue;
|
||||
}
|
||||
pw.print(" ");
|
||||
pw.print(String.valueOf(js.getJobId() + "," + js.getUid()));
|
||||
pw.println("..");
|
||||
pw.print(" #");
|
||||
js.printUniqueId(pw);
|
||||
pw.print(" from ");
|
||||
UserHandle.formatUid(pw, js.getSourceUid());
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,6 +249,12 @@ public final class JobStatus {
|
||||
return job.getId();
|
||||
}
|
||||
|
||||
public void printUniqueId(PrintWriter pw) {
|
||||
UserHandle.formatUid(pw, callingUid);
|
||||
pw.print("/");
|
||||
pw.print(job.getId());
|
||||
}
|
||||
|
||||
public int getNumFailures() {
|
||||
return numFailures;
|
||||
}
|
||||
@@ -410,6 +416,10 @@ public final class JobStatus {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean isConstraintSatisfied(int constraint) {
|
||||
return (satisfiedConstraints&constraint) != 0;
|
||||
}
|
||||
|
||||
public boolean shouldDump(int filterUid) {
|
||||
return filterUid == -1 || UserHandle.getAppId(getUid()) == filterUid
|
||||
|| UserHandle.getAppId(getSourceUid()) == filterUid;
|
||||
@@ -505,10 +515,22 @@ public final class JobStatus {
|
||||
public String toShortString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(Integer.toHexString(System.identityHashCode(this)));
|
||||
sb.append(" jId=");
|
||||
sb.append(" #");
|
||||
UserHandle.formatUid(sb, callingUid);
|
||||
sb.append("/");
|
||||
sb.append(job.getId());
|
||||
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(batteryName);
|
||||
return sb.toString();
|
||||
|
||||
@@ -20,7 +20,10 @@ import android.app.AlarmManager;
|
||||
import android.app.AlarmManager.OnAlarmListener;
|
||||
import android.content.Context;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.os.WorkSource;
|
||||
import android.util.Slog;
|
||||
import android.util.TimeUtils;
|
||||
|
||||
import com.android.server.job.JobSchedulerService;
|
||||
import com.android.server.job.StateChangedListener;
|
||||
@@ -39,9 +42,9 @@ public class TimeController extends StateController {
|
||||
private static final String TAG = "JobScheduler.Time";
|
||||
|
||||
/** 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 */
|
||||
private final String DELAY_TAG = "JobScheduler.delay";
|
||||
private final String DELAY_TAG = "*job.delay*";
|
||||
|
||||
private long mNextJobExpiredElapsedMillis;
|
||||
private long mNextDelayExpiredElapsedMillis;
|
||||
@@ -91,7 +94,8 @@ public class TimeController extends StateController {
|
||||
it.add(job);
|
||||
maybeUpdateAlarmsLocked(
|
||||
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() {
|
||||
synchronized (mLock) {
|
||||
long nextExpiryTime = Long.MAX_VALUE;
|
||||
int nextExpiryUid = 0;
|
||||
final long nowElapsedMillis = SystemClock.elapsedRealtime();
|
||||
|
||||
Iterator<JobStatus> it = mTrackedJobs.iterator();
|
||||
@@ -153,10 +158,11 @@ public class TimeController extends StateController {
|
||||
it.remove();
|
||||
} else { // Sorted by expiry time, so take the next one and stop.
|
||||
nextExpiryTime = jobDeadline;
|
||||
nextExpiryUid = job.getSourceUid();
|
||||
break;
|
||||
}
|
||||
}
|
||||
setDeadlineExpiredAlarmLocked(nextExpiryTime);
|
||||
setDeadlineExpiredAlarmLocked(nextExpiryTime, nextExpiryUid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +174,7 @@ public class TimeController extends StateController {
|
||||
synchronized (mLock) {
|
||||
final long nowElapsedMillis = SystemClock.elapsedRealtime();
|
||||
long nextDelayTime = Long.MAX_VALUE;
|
||||
int nextDelayUid = 0;
|
||||
boolean ready = false;
|
||||
Iterator<JobStatus> it = mTrackedJobs.iterator();
|
||||
while (it.hasNext()) {
|
||||
@@ -184,25 +191,29 @@ public class TimeController extends StateController {
|
||||
if (job.isReady()) {
|
||||
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) {
|
||||
nextDelayTime = jobDelayTime;
|
||||
nextDelayUid = job.getSourceUid();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ready) {
|
||||
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) {
|
||||
setDelayExpiredAlarmLocked(delayExpiredElapsed);
|
||||
setDelayExpiredAlarmLocked(delayExpiredElapsed, uid);
|
||||
}
|
||||
if (deadlineExpiredElapsed < mNextJobExpiredElapsedMillis) {
|
||||
setDeadlineExpiredAlarmLocked(deadlineExpiredElapsed);
|
||||
setDeadlineExpiredAlarmLocked(deadlineExpiredElapsed, uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,11 +222,11 @@ public class TimeController extends StateController {
|
||||
* delay will expire.
|
||||
* This alarm <b>will</b> wake up the phone.
|
||||
*/
|
||||
private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis) {
|
||||
private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, int uid) {
|
||||
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
|
||||
mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
|
||||
updateAlarmWithListenerLocked(DELAY_TAG, mNextDelayExpiredListener,
|
||||
mNextDelayExpiredElapsedMillis);
|
||||
mNextDelayExpiredElapsedMillis, uid);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,11 +234,11 @@ public class TimeController extends StateController {
|
||||
* deadline will expire.
|
||||
* This alarm <b>will</b> wake up the phone.
|
||||
*/
|
||||
private void setDeadlineExpiredAlarmLocked(long alarmTimeElapsedMillis) {
|
||||
private void setDeadlineExpiredAlarmLocked(long alarmTimeElapsedMillis, int uid) {
|
||||
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
|
||||
mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis;
|
||||
updateAlarmWithListenerLocked(DEADLINE_TAG, mDeadlineExpiredListener,
|
||||
mNextJobExpiredElapsedMillis);
|
||||
mNextJobExpiredElapsedMillis, uid);
|
||||
}
|
||||
|
||||
private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) {
|
||||
@@ -239,7 +250,7 @@ public class TimeController extends StateController {
|
||||
}
|
||||
|
||||
private void updateAlarmWithListenerLocked(String tag, OnAlarmListener listener,
|
||||
long alarmTimeElapsed) {
|
||||
long alarmTimeElapsed, int uid) {
|
||||
ensureAlarmServiceLocked();
|
||||
if (alarmTimeElapsed == Long.MAX_VALUE) {
|
||||
mAlarmService.cancel(listener);
|
||||
@@ -248,7 +259,7 @@ public class TimeController extends StateController {
|
||||
Slog.d(TAG, "Setting " + tag + " for: " + 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
|
||||
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
|
||||
final long nowElapsed = SystemClock.elapsedRealtime();
|
||||
pw.println("Alarms (" + SystemClock.elapsedRealtime() + ")");
|
||||
pw.println(
|
||||
"Next delay alarm in " + (mNextDelayExpiredElapsedMillis - nowElapsed)/1000 + "s");
|
||||
pw.println("Next deadline alarm in " + (mNextJobExpiredElapsedMillis - nowElapsed)/1000
|
||||
+ "s");
|
||||
pw.println("Tracking:");
|
||||
pw.print("Alarms: now=");
|
||||
pw.print(SystemClock.elapsedRealtime());
|
||||
pw.println();
|
||||
pw.print("Next delay alarm in ");
|
||||
TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw);
|
||||
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) {
|
||||
if (!ts.shouldDump(filterUid)) {
|
||||
continue;
|
||||
}
|
||||
pw.println(String.valueOf(ts.getJobId() + "," + ts.getUid())
|
||||
+ ": (" + (ts.hasTimingDelayConstraint() ? ts.getEarliestRunTime() : "N/A")
|
||||
+ ", " + (ts.hasDeadlineConstraint() ?ts.getLatestRunTimeElapsed() : "N/A")
|
||||
+ ")");
|
||||
pw.print(" #");
|
||||
ts.printUniqueId(pw);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user