blob: c7860368289a7cc98cfffaa1cd651cb1472ca884 [file] [log] [blame]
Matthew Williams01ac45b2014-07-22 20:44:12 -07001package com.android.server.job;
Matthew Williams3d86fd22014-05-16 18:02:17 -07002
3
4import android.content.ComponentName;
5import android.content.Context;
Christopher Tate7060b042014-06-09 19:50:00 -07006import android.app.job.JobInfo;
7import android.app.job.JobInfo.Builder;
Matthew Williams3d86fd22014-05-16 18:02:17 -07008import android.os.PersistableBundle;
Matthew Williamsfa8e5082015-10-15 15:59:12 -07009import android.os.SystemClock;
Matthew Williams3d86fd22014-05-16 18:02:17 -070010import android.test.AndroidTestCase;
11import android.test.RenamingDelegatingContext;
12import android.util.Log;
Matthew Williams01ac45b2014-07-22 20:44:12 -070013import android.util.ArraySet;
Matthew Williams3d86fd22014-05-16 18:02:17 -070014
Christopher Tate7060b042014-06-09 19:50:00 -070015import com.android.server.job.controllers.JobStatus;
Matthew Williams3d86fd22014-05-16 18:02:17 -070016
Matthew Williams01ac45b2014-07-22 20:44:12 -070017import java.util.Iterator;
Matthew Williams3d86fd22014-05-16 18:02:17 -070018
Matthew Williams3d86fd22014-05-16 18:02:17 -070019/**
20 * Test reading and writing correctly from file.
21 */
Matthew Williams01ac45b2014-07-22 20:44:12 -070022public class JobStoreTest extends AndroidTestCase {
Matthew Williams3d86fd22014-05-16 18:02:17 -070023 private static final String TAG = "TaskStoreTest";
24 private static final String TEST_PREFIX = "_test_";
Matthew Williams01ac45b2014-07-22 20:44:12 -070025
Matthew Williams3d86fd22014-05-16 18:02:17 -070026 private static final int SOME_UID = 34234;
27 private ComponentName mComponent;
Matthew Williams01ac45b2014-07-22 20:44:12 -070028 private static final long IO_WAIT = 1000L;
Matthew Williams3d86fd22014-05-16 18:02:17 -070029
Christopher Tate7060b042014-06-09 19:50:00 -070030 JobStore mTaskStoreUnderTest;
Matthew Williams3d86fd22014-05-16 18:02:17 -070031 Context mTestContext;
Matthew Williams3d86fd22014-05-16 18:02:17 -070032
33 @Override
34 public void setUp() throws Exception {
35 mTestContext = new RenamingDelegatingContext(getContext(), TEST_PREFIX);
36 Log.d(TAG, "Saving tasks to '" + mTestContext.getFilesDir() + "'");
Matthew Williams01ac45b2014-07-22 20:44:12 -070037 mTaskStoreUnderTest =
38 JobStore.initAndGetForTesting(mTestContext, mTestContext.getFilesDir());
Matthew Williams3d86fd22014-05-16 18:02:17 -070039 mComponent = new ComponentName(getContext().getPackageName(), StubClass.class.getName());
40 }
41
42 @Override
43 public void tearDown() throws Exception {
44 mTaskStoreUnderTest.clear();
45 }
46
47 public void testMaybeWriteStatusToDisk() throws Exception {
48 int taskId = 5;
49 long runByMillis = 20000L; // 20s
50 long runFromMillis = 2000L; // 2s
51 long initialBackoff = 10000L; // 10s
52
Christopher Tate7060b042014-06-09 19:50:00 -070053 final JobInfo task = new Builder(taskId, mComponent)
Matthew Williams3d86fd22014-05-16 18:02:17 -070054 .setRequiresCharging(true)
Matthew Williamsd1c06752014-08-22 14:15:28 -070055 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
56 .setBackoffCriteria(initialBackoff, JobInfo.BACKOFF_POLICY_EXPONENTIAL)
Matthew Williams3d86fd22014-05-16 18:02:17 -070057 .setOverrideDeadline(runByMillis)
58 .setMinimumLatency(runFromMillis)
Matthew Williamsd1c06752014-08-22 14:15:28 -070059 .setPersisted(true)
Matthew Williams3d86fd22014-05-16 18:02:17 -070060 .build();
Dianne Hackbornb0001f62016-02-16 10:30:33 -080061 final JobStatus ts = JobStatus.createFromJobInfo(task, SOME_UID, null, -1);
Matthew Williams3d86fd22014-05-16 18:02:17 -070062 mTaskStoreUnderTest.add(ts);
63 Thread.sleep(IO_WAIT);
64 // Manually load tasks from xml file.
Matthew Williams01ac45b2014-07-22 20:44:12 -070065 final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
66 mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
67
68 assertEquals("Didn't get expected number of persisted tasks.", 1, jobStatusSet.size());
69 final JobStatus loadedTaskStatus = jobStatusSet.iterator().next();
70 assertTasksEqual(task, loadedTaskStatus.getJob());
Matthew Williams48a30db2014-09-23 13:39:36 -070071 assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(ts));
Matthew Williams01ac45b2014-07-22 20:44:12 -070072 assertEquals("Different uids.", SOME_UID, loadedTaskStatus.getUid());
73 compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
74 ts.getEarliestRunTime(), loadedTaskStatus.getEarliestRunTime());
75 compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
76 ts.getLatestRunTimeElapsed(), loadedTaskStatus.getLatestRunTimeElapsed());
Matthew Williams3d86fd22014-05-16 18:02:17 -070077
78 }
79
80 public void testWritingTwoFilesToDisk() throws Exception {
Christopher Tate7060b042014-06-09 19:50:00 -070081 final JobInfo task1 = new Builder(8, mComponent)
Matthew Williams3d86fd22014-05-16 18:02:17 -070082 .setRequiresDeviceIdle(true)
83 .setPeriodic(10000L)
84 .setRequiresCharging(true)
Matthew Williamsd1c06752014-08-22 14:15:28 -070085 .setPersisted(true)
Matthew Williams3d86fd22014-05-16 18:02:17 -070086 .build();
Christopher Tate7060b042014-06-09 19:50:00 -070087 final JobInfo task2 = new Builder(12, mComponent)
Matthew Williams3d86fd22014-05-16 18:02:17 -070088 .setMinimumLatency(5000L)
Matthew Williamsd1c06752014-08-22 14:15:28 -070089 .setBackoffCriteria(15000L, JobInfo.BACKOFF_POLICY_LINEAR)
Matthew Williams3d86fd22014-05-16 18:02:17 -070090 .setOverrideDeadline(30000L)
Matthew Williamsd1c06752014-08-22 14:15:28 -070091 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
92 .setPersisted(true)
Matthew Williams3d86fd22014-05-16 18:02:17 -070093 .build();
Dianne Hackbornb0001f62016-02-16 10:30:33 -080094 final JobStatus taskStatus1 = JobStatus.createFromJobInfo(task1, SOME_UID, null, -1);
95 final JobStatus taskStatus2 = JobStatus.createFromJobInfo(task2, SOME_UID, null, -1);
Matthew Williams3d86fd22014-05-16 18:02:17 -070096 mTaskStoreUnderTest.add(taskStatus1);
97 mTaskStoreUnderTest.add(taskStatus2);
98 Thread.sleep(IO_WAIT);
Matthew Williams3d86fd22014-05-16 18:02:17 -070099
Matthew Williams01ac45b2014-07-22 20:44:12 -0700100 final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
101 mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
102 assertEquals("Incorrect # of persisted tasks.", 2, jobStatusSet.size());
103 Iterator<JobStatus> it = jobStatusSet.iterator();
104 JobStatus loaded1 = it.next();
105 JobStatus loaded2 = it.next();
Matthew Williamsfa8e5082015-10-15 15:59:12 -0700106
107 // Reverse them so we know which comparison to make.
108 if (loaded1.getJobId() != 8) {
109 JobStatus tmp = loaded1;
110 loaded1 = loaded2;
111 loaded2 = tmp;
112 }
113
Matthew Williams01ac45b2014-07-22 20:44:12 -0700114 assertTasksEqual(task1, loaded1.getJob());
115 assertTasksEqual(task2, loaded2.getJob());
Matthew Williams48a30db2014-09-23 13:39:36 -0700116 assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(taskStatus1));
117 assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(taskStatus2));
Matthew Williams01ac45b2014-07-22 20:44:12 -0700118 // Check that the loaded task has the correct runtimes.
119 compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
120 taskStatus1.getEarliestRunTime(), loaded1.getEarliestRunTime());
121 compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
122 taskStatus1.getLatestRunTimeElapsed(), loaded1.getLatestRunTimeElapsed());
123 compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
124 taskStatus2.getEarliestRunTime(), loaded2.getEarliestRunTime());
125 compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
126 taskStatus2.getLatestRunTimeElapsed(), loaded2.getLatestRunTimeElapsed());
Matthew Williams3d86fd22014-05-16 18:02:17 -0700127
128 }
129
130 public void testWritingTaskWithExtras() throws Exception {
Christopher Tate7060b042014-06-09 19:50:00 -0700131 JobInfo.Builder b = new Builder(8, mComponent)
Matthew Williams3d86fd22014-05-16 18:02:17 -0700132 .setRequiresDeviceIdle(true)
133 .setPeriodic(10000L)
Matthew Williams900c67f2014-07-09 12:46:53 -0700134 .setRequiresCharging(true)
Matthew Williamsd1c06752014-08-22 14:15:28 -0700135 .setPersisted(true);
Matthew Williams3d86fd22014-05-16 18:02:17 -0700136
137 PersistableBundle extras = new PersistableBundle();
138 extras.putDouble("hello", 3.2);
139 extras.putString("hi", "there");
140 extras.putInt("into", 3);
141 b.setExtras(extras);
Christopher Tate7060b042014-06-09 19:50:00 -0700142 final JobInfo task = b.build();
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800143 JobStatus taskStatus = JobStatus.createFromJobInfo(task, SOME_UID, null, -1);
Matthew Williams3d86fd22014-05-16 18:02:17 -0700144
145 mTaskStoreUnderTest.add(taskStatus);
146 Thread.sleep(IO_WAIT);
Matthew Williams3d86fd22014-05-16 18:02:17 -0700147
Matthew Williams01ac45b2014-07-22 20:44:12 -0700148 final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
149 mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
150 assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
151 JobStatus loaded = jobStatusSet.iterator().next();
152 assertTasksEqual(task, loaded.getJob());
Matthew Williams3d86fd22014-05-16 18:02:17 -0700153 }
Shreyas Basarge8e64e2e2016-02-12 15:49:31 +0000154 public void testWritingTaskWithSourcePackage() throws Exception {
155 JobInfo.Builder b = new Builder(8, mComponent)
156 .setRequiresDeviceIdle(true)
157 .setPeriodic(10000L)
158 .setRequiresCharging(true)
159 .setPersisted(true);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800160 JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID,
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800161 "com.google.android.gms", 0);
Shreyas Basarge8e64e2e2016-02-12 15:49:31 +0000162
163 mTaskStoreUnderTest.add(taskStatus);
164 Thread.sleep(IO_WAIT);
165
166 final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
167 mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
168 assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
169 JobStatus loaded = jobStatusSet.iterator().next();
170 assertEquals("Source package not equal.", loaded.getSourcePackageName(),
171 taskStatus.getSourcePackageName());
172 assertEquals("Source user not equal.", loaded.getSourceUserId(),
173 taskStatus.getSourceUserId());
174 }
175
176 public void testWritingTaskWithFlex() throws Exception {
177 JobInfo.Builder b = new Builder(8, mComponent)
178 .setRequiresDeviceIdle(true)
179 .setPeriodic(5*60*60*1000, 1*60*60*1000)
180 .setRequiresCharging(true)
181 .setPersisted(true);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800182 JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1);
Shreyas Basarge8e64e2e2016-02-12 15:49:31 +0000183
184 mTaskStoreUnderTest.add(taskStatus);
185 Thread.sleep(IO_WAIT);
186
187 final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
188 mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
189 assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
190 JobStatus loaded = jobStatusSet.iterator().next();
191 assertEquals("Period not equal.", loaded.getJob().getIntervalMillis(),
192 taskStatus.getJob().getIntervalMillis());
193 assertEquals("Flex not equal.", loaded.getJob().getFlexMillis(),
194 taskStatus.getJob().getFlexMillis());
195 }
Matthew Williams3d86fd22014-05-16 18:02:17 -0700196
Matthew Williamsfa8e5082015-10-15 15:59:12 -0700197 public void testMassivePeriodClampedOnRead() throws Exception {
Shreyas Basarge8e64e2e2016-02-12 15:49:31 +0000198 final long ONE_HOUR = 60*60*1000L; // flex
199 final long TWO_HOURS = 2 * ONE_HOUR; // period
Matthew Williamsfa8e5082015-10-15 15:59:12 -0700200 JobInfo.Builder b = new Builder(8, mComponent)
Shreyas Basarge8e64e2e2016-02-12 15:49:31 +0000201 .setPeriodic(TWO_HOURS, ONE_HOUR)
Matthew Williamsfa8e5082015-10-15 15:59:12 -0700202 .setPersisted(true);
203 final long invalidLateRuntimeElapsedMillis =
Shreyas Basarge8e64e2e2016-02-12 15:49:31 +0000204 SystemClock.elapsedRealtime() + (TWO_HOURS * ONE_HOUR) + TWO_HOURS; // > period+flex
Matthew Williamsfa8e5082015-10-15 15:59:12 -0700205 final long invalidEarlyRuntimeElapsedMillis =
Shreyas Basarge8e64e2e2016-02-12 15:49:31 +0000206 invalidLateRuntimeElapsedMillis - TWO_HOURS; // Early is (late - period).
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800207 final JobStatus js = new JobStatus(b.build(), SOME_UID, "somePackage",
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800208 0 /* sourceUserId */,
Matthew Williamsfa8e5082015-10-15 15:59:12 -0700209 invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis);
210
211 mTaskStoreUnderTest.add(js);
212 Thread.sleep(IO_WAIT);
213
214 final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
215 mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
216 assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
217 JobStatus loaded = jobStatusSet.iterator().next();
218
219 // Assert early runtime was clamped to be under now + period. We can do <= here b/c we'll
220 // call SystemClock.elapsedRealtime after doing the disk i/o.
221 final long newNowElapsed = SystemClock.elapsedRealtime();
222 assertTrue("Early runtime wasn't correctly clamped.",
Shreyas Basarge8e64e2e2016-02-12 15:49:31 +0000223 loaded.getEarliestRunTime() <= newNowElapsed + TWO_HOURS);
224 // Assert late runtime was clamped to be now + period + flex.
Matthew Williamsfa8e5082015-10-15 15:59:12 -0700225 assertTrue("Early runtime wasn't correctly clamped.",
Shreyas Basarge8e64e2e2016-02-12 15:49:31 +0000226 loaded.getEarliestRunTime() <= newNowElapsed + TWO_HOURS + ONE_HOUR);
Shreyas Basarge5db09082016-01-07 13:38:29 +0000227 }
228
229 public void testPriorityPersisted() throws Exception {
230 JobInfo.Builder b = new Builder(92, mComponent)
231 .setOverrideDeadline(5000)
232 .setPriority(42)
233 .setPersisted(true);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800234 final JobStatus js = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1);
Shreyas Basarge5db09082016-01-07 13:38:29 +0000235 mTaskStoreUnderTest.add(js);
236 Thread.sleep(IO_WAIT);
237 final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
238 mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
239 JobStatus loaded = jobStatusSet.iterator().next();
240 assertEquals("Priority not correctly persisted.", 42, loaded.getPriority());
Matthew Williamsfa8e5082015-10-15 15:59:12 -0700241 }
242
Matthew Williams3d86fd22014-05-16 18:02:17 -0700243 /**
Shreyas Basarge7ef490f2015-12-03 16:45:22 +0000244 * Test that non persisted job is not written to disk.
245 */
246 public void testNonPersistedTaskIsNotPersisted() throws Exception {
247 JobInfo.Builder b = new Builder(42, mComponent)
248 .setOverrideDeadline(10000)
249 .setPersisted(false);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800250 JobStatus jsNonPersisted = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1);
Shreyas Basarge7ef490f2015-12-03 16:45:22 +0000251 mTaskStoreUnderTest.add(jsNonPersisted);
252 b = new Builder(43, mComponent)
253 .setOverrideDeadline(10000)
254 .setPersisted(true);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800255 JobStatus jsPersisted = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1);
Shreyas Basarge7ef490f2015-12-03 16:45:22 +0000256 mTaskStoreUnderTest.add(jsPersisted);
257 Thread.sleep(IO_WAIT);
258 final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
259 mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
260 assertEquals("Job count is incorrect.", 1, jobStatusSet.size());
261 JobStatus jobStatus = jobStatusSet.iterator().next();
262 assertEquals("Wrong job persisted.", 43, jobStatus.getJobId());
263 }
264
265 /**
Matthew Williams3d86fd22014-05-16 18:02:17 -0700266 * Helper function to throw an error if the provided task and TaskStatus objects are not equal.
267 */
Christopher Tate7060b042014-06-09 19:50:00 -0700268 private void assertTasksEqual(JobInfo first, JobInfo second) {
Matthew Williams3d86fd22014-05-16 18:02:17 -0700269 assertEquals("Different task ids.", first.getId(), second.getId());
270 assertEquals("Different components.", first.getService(), second.getService());
271 assertEquals("Different periodic status.", first.isPeriodic(), second.isPeriodic());
272 assertEquals("Different period.", first.getIntervalMillis(), second.getIntervalMillis());
273 assertEquals("Different inital backoff.", first.getInitialBackoffMillis(),
274 second.getInitialBackoffMillis());
275 assertEquals("Different backoff policy.", first.getBackoffPolicy(),
276 second.getBackoffPolicy());
277
278 assertEquals("Invalid charging constraint.", first.isRequireCharging(),
279 second.isRequireCharging());
280 assertEquals("Invalid idle constraint.", first.isRequireDeviceIdle(),
281 second.isRequireDeviceIdle());
282 assertEquals("Invalid unmetered constraint.",
Matthew Williamsd1c06752014-08-22 14:15:28 -0700283 first.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED,
284 second.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED);
Matthew Williams3d86fd22014-05-16 18:02:17 -0700285 assertEquals("Invalid connectivity constraint.",
Matthew Williamsd1c06752014-08-22 14:15:28 -0700286 first.getNetworkType() == JobInfo.NETWORK_TYPE_ANY,
287 second.getNetworkType() == JobInfo.NETWORK_TYPE_ANY);
Matthew Williams3d86fd22014-05-16 18:02:17 -0700288 assertEquals("Invalid deadline constraint.",
289 first.hasLateConstraint(),
290 second.hasLateConstraint());
291 assertEquals("Invalid delay constraint.",
292 first.hasEarlyConstraint(),
293 second.hasEarlyConstraint());
294 assertEquals("Extras don't match",
295 first.getExtras().toString(), second.getExtras().toString());
296 }
297
298 /**
299 * When comparing timestamps before and after DB read/writes (to make sure we're saving/loading
300 * the correct values), there is some latency involved that terrorises a naive assertEquals().
301 * We define a <code>DELTA_MILLIS</code> as a function variable here to make this comparision
302 * more reasonable.
303 */
304 private void compareTimestampsSubjectToIoLatency(String error, long ts1, long ts2) {
305 final long DELTA_MILLIS = 700L; // We allow up to 700ms of latency for IO read/writes.
306 assertTrue(error, Math.abs(ts1 - ts2) < DELTA_MILLIS + IO_WAIT);
307 }
308
309 private static class StubClass {}
310
Matthew Williams01ac45b2014-07-22 20:44:12 -0700311}