JobStatus.java revision 1b8b3aa265190e84467f740e99a0ade3a0e3cd67
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.job.controllers;
18
19import android.app.job.JobInfo;
20import android.content.ComponentName;
21import android.os.PersistableBundle;
22import android.os.SystemClock;
23import android.os.UserHandle;
24
25import java.io.PrintWriter;
26import java.util.concurrent.atomic.AtomicBoolean;
27
28/**
29 * Uniquely identifies a job internally.
30 * Created from the public {@link android.app.job.JobInfo} object when it lands on the scheduler.
31 * Contains current state of the requirements of the job, as well as a function to evaluate
32 * whether it's ready to run.
33 * This object is shared among the various controllers - hence why the different fields are atomic.
34 * This isn't strictly necessary because each controller is only interested in a specific field,
35 * and the receivers that are listening for global state change will all run on the main looper,
36 * but we don't enforce that so this is safer.
37 * @hide
38 */
39public class JobStatus {
40    public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
41    public static final long NO_EARLIEST_RUNTIME = 0L;
42
43    final JobInfo job;
44    final int uId;
45
46    /** At reschedule time we need to know whether to update job on disk. */
47    final boolean persisted;
48
49    // Constraints.
50    final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
51    final AtomicBoolean timeDelayConstraintSatisfied = new AtomicBoolean();
52    final AtomicBoolean deadlineConstraintSatisfied = new AtomicBoolean();
53    final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean();
54    final AtomicBoolean unmeteredConstraintSatisfied = new AtomicBoolean();
55    final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean();
56
57    /**
58     * Earliest point in the future at which this job will be eligible to run. A value of 0
59     * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
60     */
61    private long earliestRunTimeElapsedMillis;
62    /**
63     * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE}
64     * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
65     */
66    private long latestRunTimeElapsedMillis;
67    /** How many times this job has failed, used to compute back-off. */
68    private final int numFailures;
69
70    /** Provide a handle to the service that this job will be run on. */
71    public int getServiceToken() {
72        return uId;
73    }
74
75    private JobStatus(JobInfo job, int uId, boolean persisted, int numFailures) {
76        this.job = job;
77        this.uId = uId;
78        this.numFailures = numFailures;
79        this.persisted = persisted;
80    }
81
82    /** Create a newly scheduled job. */
83    public JobStatus(JobInfo job, int uId, boolean persisted) {
84        this(job, uId, persisted, 0);
85
86        final long elapsedNow = SystemClock.elapsedRealtime();
87
88        if (job.isPeriodic()) {
89            earliestRunTimeElapsedMillis = elapsedNow;
90            latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
91        } else {
92            earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
93                    elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
94            latestRunTimeElapsedMillis = job.hasLateConstraint() ?
95                    elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
96        }
97    }
98
99    /**
100     * Create a new JobStatus that was loaded from disk. We ignore the provided
101     * {@link android.app.job.JobInfo} time criteria because we can load a persisted periodic job
102     * from the {@link com.android.server.job.JobStore} and still want to respect its
103     * wallclock runtime rather than resetting it on every boot.
104     * We consider a freshly loaded job to no longer be in back-off.
105     */
106    public JobStatus(JobInfo job, int uId, long earliestRunTimeElapsedMillis,
107                      long latestRunTimeElapsedMillis) {
108        this(job, uId, true, 0);
109
110        this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
111        this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
112    }
113
114    /** Create a new job to be rescheduled with the provided parameters. */
115    public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis,
116                      long newLatestRuntimeElapsedMillis, int backoffAttempt) {
117        this(rescheduling.job, rescheduling.getUid(), rescheduling.isPersisted(), backoffAttempt);
118
119        earliestRunTimeElapsedMillis = newEarliestRuntimeElapsedMillis;
120        latestRunTimeElapsedMillis = newLatestRuntimeElapsedMillis;
121    }
122
123    public JobInfo getJob() {
124        return job;
125    }
126
127    public int getJobId() {
128        return job.getId();
129    }
130
131    public int getNumFailures() {
132        return numFailures;
133    }
134
135    public ComponentName getServiceComponent() {
136        return job.getService();
137    }
138
139    public int getUserId() {
140        return UserHandle.getUserId(uId);
141    }
142
143    public int getUid() {
144        return uId;
145    }
146
147    public PersistableBundle getExtras() {
148        return job.getExtras();
149    }
150
151    public boolean hasConnectivityConstraint() {
152        return job.getNetworkCapabilities() == JobInfo.NetworkType.ANY;
153    }
154
155    public boolean hasUnmeteredConstraint() {
156        return job.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED;
157    }
158
159    public boolean hasChargingConstraint() {
160        return job.isRequireCharging();
161    }
162
163    public boolean hasTimingDelayConstraint() {
164        return earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME;
165    }
166
167    public boolean hasDeadlineConstraint() {
168        return latestRunTimeElapsedMillis != NO_LATEST_RUNTIME;
169    }
170
171    public boolean hasIdleConstraint() {
172        return job.isRequireDeviceIdle();
173    }
174
175    public long getEarliestRunTime() {
176        return earliestRunTimeElapsedMillis;
177    }
178
179    public long getLatestRunTimeElapsed() {
180        return latestRunTimeElapsedMillis;
181    }
182
183    public boolean isPersisted() {
184        return persisted;
185    }
186    /**
187     * @return Whether or not this job is ready to run, based on its requirements.
188     */
189    public synchronized boolean isReady() {
190        return (!hasChargingConstraint() || chargingConstraintSatisfied.get())
191                && (!hasTimingDelayConstraint() || timeDelayConstraintSatisfied.get())
192                && (!hasConnectivityConstraint() || connectivityConstraintSatisfied.get())
193                && (!hasUnmeteredConstraint() || unmeteredConstraintSatisfied.get())
194                && (!hasIdleConstraint() || idleConstraintSatisfied.get())
195                // Also ready if the deadline has expired - special case.
196                || (hasDeadlineConstraint() && deadlineConstraintSatisfied.get());
197    }
198
199    public boolean matches(int uid, int jobId) {
200        return this.job.getId() == jobId && this.uId == uid;
201    }
202
203    @Override
204    public String toString() {
205        return String.valueOf(hashCode()).substring(0, 3) + ".."
206                + ":[" + job.getService()
207                + ",jId=" + job.getId()
208                + ",R=(" + earliestRunTimeElapsedMillis + "," + latestRunTimeElapsedMillis + ")"
209                + ",N=" + job.getNetworkCapabilities() + ",C=" + job.isRequireCharging()
210                + ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures
211                + (isReady() ? "(READY)" : "")
212                + "]";
213    }
214    // Dumpsys infrastructure
215    public void dump(PrintWriter pw, String prefix) {
216        pw.println(this.toString());
217    }
218}
219