JobScheduler only run jobs for started users.
BUG: 12876556 Minor changes to test app to make persisting an option. Change-Id: I1b40347878ec5ca44cd717ebfeb544f6c58473b5
This commit is contained in:
@@ -26,6 +26,9 @@ import android.os.PersistableBundle;
|
||||
* Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the
|
||||
* parameters required to schedule work against the calling application. These are constructed
|
||||
* using the {@link JobInfo.Builder}.
|
||||
* You must specify at least one sort of constraint on the JobInfo object that you are creating.
|
||||
* The goal here is to provide the scheduler with high-level semantics about the work you want to
|
||||
* accomplish. Doing otherwise with throw an exception in your app.
|
||||
*/
|
||||
public class JobInfo implements Parcelable {
|
||||
public interface NetworkType {
|
||||
@@ -434,7 +437,7 @@ public class JobInfo implements Parcelable {
|
||||
* @return The job object to hand to the JobScheduler. This object is immutable.
|
||||
*/
|
||||
public JobInfo build() {
|
||||
// Allow tasks with no constraints. What am I, a database?
|
||||
// Allow jobs with no constraints - What am I, a database?
|
||||
if (!mHasEarlyConstraint && !mHasLateConstraint && !mRequiresCharging &&
|
||||
!mRequiresDeviceIdle && mNetworkCapabilities == NetworkType.NONE) {
|
||||
throw new IllegalArgumentException("You're trying to build a job with no " +
|
||||
|
||||
@@ -117,6 +117,8 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
*/
|
||||
final ArrayList<JobStatus> mPendingJobs = new ArrayList<JobStatus>();
|
||||
|
||||
final ArrayList<Integer> mStartedUsers = new ArrayList();
|
||||
|
||||
final JobHandler mHandler;
|
||||
final JobSchedulerStub mJobSchedulerStub;
|
||||
|
||||
@@ -151,6 +153,18 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onStartUser(int userHandle) {
|
||||
mStartedUsers.add(userHandle);
|
||||
// Let's kick any outstanding jobs for this user.
|
||||
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopUser(int userHandle) {
|
||||
mStartedUsers.remove(Integer.valueOf(userHandle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point from client to schedule the provided job.
|
||||
* This cancels the job if it's already been scheduled, and replaces it with the one provided.
|
||||
@@ -610,9 +624,20 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
* - It's ready.
|
||||
* - It's not pending.
|
||||
* - It's not already running on a JSC.
|
||||
* - The user that requested the job is running.
|
||||
*/
|
||||
private boolean isReadyToBeExecutedLocked(JobStatus job) {
|
||||
return job.isReady() && !mPendingJobs.contains(job) && !isCurrentlyActiveLocked(job);
|
||||
final boolean jobReady = job.isReady();
|
||||
final boolean jobPending = mPendingJobs.contains(job);
|
||||
final boolean jobActive = isCurrentlyActiveLocked(job);
|
||||
final boolean userRunning = mStartedUsers.contains(job.getUserId());
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
|
||||
+ " ready=" + jobReady + " pending=" + jobPending
|
||||
+ " active=" + jobActive + " userRunning=" + userRunning);
|
||||
}
|
||||
return userRunning && jobReady && !jobPending && !jobActive;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -795,6 +820,11 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
|
||||
void dumpInternal(PrintWriter pw) {
|
||||
synchronized (mJobs) {
|
||||
pw.print("Started users: ");
|
||||
for (int i=0; i<mStartedUsers.size(); i++) {
|
||||
pw.print("u" + mStartedUsers.get(i) + " ");
|
||||
}
|
||||
pw.println();
|
||||
pw.println("Registered jobs:");
|
||||
if (mJobs.size() > 0) {
|
||||
ArraySet<JobStatus> jobs = mJobs.getJobs();
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.ComponentName;
|
||||
import android.os.PersistableBundle;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -41,6 +42,7 @@ public class JobStatus {
|
||||
public static final long NO_EARLIEST_RUNTIME = 0L;
|
||||
|
||||
final JobInfo job;
|
||||
/** Uid of the package requesting this job. */
|
||||
final int uId;
|
||||
final String name;
|
||||
final String tag;
|
||||
@@ -214,12 +216,39 @@ public class JobStatus {
|
||||
return String.valueOf(hashCode()).substring(0, 3) + ".."
|
||||
+ ":[" + job.getService()
|
||||
+ ",jId=" + job.getId()
|
||||
+ ",R=(" + earliestRunTimeElapsedMillis + "," + latestRunTimeElapsedMillis + ")"
|
||||
+ ",u" + getUserId()
|
||||
+ ",R=(" + formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME)
|
||||
+ "," + formatRunTime(latestRunTimeElapsedMillis, NO_LATEST_RUNTIME) + ")"
|
||||
+ ",N=" + job.getNetworkCapabilities() + ",C=" + job.isRequireCharging()
|
||||
+ ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures
|
||||
+ ",P=" + job.isPersisted()
|
||||
+ (isReady() ? "(READY)" : "")
|
||||
+ "]";
|
||||
}
|
||||
|
||||
private String formatRunTime(long runtime, long defaultValue) {
|
||||
if (runtime == defaultValue) {
|
||||
return "none";
|
||||
} else {
|
||||
long elapsedNow = SystemClock.elapsedRealtime();
|
||||
long nextRuntime = runtime - elapsedNow;
|
||||
if (nextRuntime > 0) {
|
||||
return DateUtils.formatElapsedTime(nextRuntime / 1000);
|
||||
} else {
|
||||
return "-" + DateUtils.formatElapsedTime(nextRuntime / -1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to identify a job uniquely without pulling all the data that
|
||||
* {@link #toString()} returns.
|
||||
*/
|
||||
public String toShortString() {
|
||||
return job.getService().flattenToShortString() + " jId=" + job.getId() +
|
||||
", u" + getUserId();
|
||||
}
|
||||
|
||||
// Dumpsys infrastructure
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
pw.println(this.toString());
|
||||
|
||||
@@ -141,6 +141,20 @@
|
||||
android:id="@+id/checkbox_idle"
|
||||
android:text="@string/idle_mode_text"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/persisted_caption"
|
||||
android:layout_marginRight="15dp"/>
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/checkbox_persisted"
|
||||
android:text="@string/persisted_mode_text"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<Button
|
||||
|
||||
@@ -27,6 +27,7 @@ limitations under the License.
|
||||
<string name="charging_caption">Charging:</string>
|
||||
<string name="charging_text">Requires device plugged in.</string>
|
||||
<string name="idle_caption">Idle:</string>
|
||||
<string name="persisted_caption">Persisted:</string>
|
||||
<string name="constraints">Constraints</string>
|
||||
<string name="connectivity">Connectivity:</string>
|
||||
<string name="any">Any</string>
|
||||
@@ -34,4 +35,5 @@ limitations under the License.
|
||||
<string name="timing">Timing:</string>
|
||||
<string name="delay">Delay:</string>
|
||||
<string name="deadline">Deadline:</string>
|
||||
<string name="persisted_mode_text">Persisted:</string>
|
||||
</resources>
|
||||
|
||||
@@ -65,6 +65,8 @@ public class MainActivity extends Activity {
|
||||
mAnyConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_any);
|
||||
mRequiresChargingCheckBox = (CheckBox) findViewById(R.id.checkbox_charging);
|
||||
mRequiresIdleCheckbox = (CheckBox) findViewById(R.id.checkbox_idle);
|
||||
mIsPersistedCheckbox = (CheckBox) findViewById(R.id.checkbox_persisted);
|
||||
|
||||
mServiceComponent = new ComponentName(this, TestJobService.class);
|
||||
// Start service and provide it a way to communicate with us.
|
||||
Intent startServiceIntent = new Intent(this, TestJobService.class);
|
||||
@@ -85,6 +87,7 @@ public class MainActivity extends Activity {
|
||||
RadioButton mAnyConnectivityRadioButton;
|
||||
CheckBox mRequiresChargingCheckBox;
|
||||
CheckBox mRequiresIdleCheckbox;
|
||||
CheckBox mIsPersistedCheckbox;
|
||||
|
||||
ComponentName mServiceComponent;
|
||||
/** Service object to interact scheduled jobs. */
|
||||
@@ -146,7 +149,7 @@ public class MainActivity extends Activity {
|
||||
}
|
||||
builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked());
|
||||
builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked());
|
||||
|
||||
builder.setIsPersisted(mIsPersistedCheckbox.isChecked());
|
||||
mTestService.scheduleJob(builder.build());
|
||||
|
||||
}
|
||||
|
||||
@@ -90,31 +90,6 @@ public class TestJobService extends JobService {
|
||||
mActivity.onReceivedStartJob(params);
|
||||
}
|
||||
|
||||
// Spin off a new task on a separate thread for a couple seconds.
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
try {
|
||||
Log.d(TAG, "Sleeping for 3 seconds.");
|
||||
Thread.sleep(3000L);
|
||||
} catch (InterruptedException e) {}
|
||||
final JobParameters params = jobParamsMap.get(currId);
|
||||
Log.d(TAG, "Pulled :" + currId + " " + params);
|
||||
jobFinished(params, false);
|
||||
|
||||
Log.d(TAG, "Rescheduling new job: " + params.getJobId());
|
||||
scheduleJob(
|
||||
new JobInfo.Builder(params.getJobId(),
|
||||
new ComponentName(getBaseContext(), TestJobService.class))
|
||||
.setMinimumLatency(2000L)
|
||||
.setOverrideDeadline(3000L)
|
||||
.setRequiresCharging(true)
|
||||
.build()
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user