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