JobStatus.java revision fdb1956ff71ff57fcdaafaaeb7f42c19de3d7c2f
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    final String name;
46    final String tag;
47
48    // Constraints.
49    final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
50    final AtomicBoolean timeDelayConstraintSatisfied = new AtomicBoolean();
51    final AtomicBoolean deadlineConstraintSatisfied = new AtomicBoolean();
52    final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean();
53    final AtomicBoolean unmeteredConstraintSatisfied = new AtomicBoolean();
54    final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean();
55
56    /**
57     * Earliest point in the future at which this job will be eligible to run. A value of 0
58     * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
59     */
60    private long earliestRunTimeElapsedMillis;
61    /**
62     * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE}
63     * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
64     */
65    private long latestRunTimeElapsedMillis;
66    /** How many times this job has failed, used to compute back-off. */
67    private final int numFailures;
68
69    /** Provide a handle to the service that this job will be run on. */
70    public int getServiceToken() {
71        return uId;
72    }
73
74    private JobStatus(JobInfo job, int uId, int numFailures) {
75        this.job = job;
76        this.uId = uId;
77        this.name = job.getService().flattenToShortString();
78        this.tag = "*job*/" + this.name;
79        this.numFailures = numFailures;
80    }
81
82    /** Create a newly scheduled job. */
83    public JobStatus(JobInfo job, int uId) {
84        this(job, uId, 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, 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(), 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 String getName() {
148        return name;
149    }
150
151    public String getTag() {
152        return tag;
153    }
154
155    public PersistableBundle getExtras() {
156        return job.getExtras();
157    }
158
159    public boolean hasConnectivityConstraint() {
160        return job.getNetworkCapabilities() == JobInfo.NetworkType.ANY;
161    }
162
163    public boolean hasUnmeteredConstraint() {
164        return job.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED;
165    }
166
167    public boolean hasChargingConstraint() {
168        return job.isRequireCharging();
169    }
170
171    public boolean hasTimingDelayConstraint() {
172        return earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME;
173    }
174
175    public boolean hasDeadlineConstraint() {
176        return latestRunTimeElapsedMillis != NO_LATEST_RUNTIME;
177    }
178
179    public boolean hasIdleConstraint() {
180        return job.isRequireDeviceIdle();
181    }
182
183    public boolean isPersisted() {
184        return job.isPersisted();
185    }
186
187    public long getEarliestRunTime() {
188        return earliestRunTimeElapsedMillis;
189    }
190
191    public long getLatestRunTimeElapsed() {
192        return latestRunTimeElapsedMillis;
193    }
194
195    /**
196     * @return Whether or not this job is ready to run, based on its requirements.
197     */
198    public synchronized boolean isReady() {
199        return (!hasChargingConstraint() || chargingConstraintSatisfied.get())
200                && (!hasTimingDelayConstraint() || timeDelayConstraintSatisfied.get())
201                && (!hasConnectivityConstraint() || connectivityConstraintSatisfied.get())
202                && (!hasUnmeteredConstraint() || unmeteredConstraintSatisfied.get())
203                && (!hasIdleConstraint() || idleConstraintSatisfied.get())
204                // Also ready if the deadline has expired - special case.
205                || (hasDeadlineConstraint() && deadlineConstraintSatisfied.get());
206    }
207
208    public boolean matches(int uid, int jobId) {
209        return this.job.getId() == jobId && this.uId == uid;
210    }
211
212    @Override
213    public String toString() {
214        return String.valueOf(hashCode()).substring(0, 3) + ".."
215                + ":[" + job.getService()
216                + ",jId=" + job.getId()
217                + ",R=(" + earliestRunTimeElapsedMillis + "," + latestRunTimeElapsedMillis + ")"
218                + ",N=" + job.getNetworkCapabilities() + ",C=" + job.isRequireCharging()
219                + ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures
220                + (isReady() ? "(READY)" : "")
221                + "]";
222    }
223    // Dumpsys infrastructure
224    public void dump(PrintWriter pw, String prefix) {
225        pw.println(this.toString());
226    }
227}
228