1 /*
2  * Copyright (C) 2020 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 
17 package android.jobscheduler.cts;
18 
19 import android.app.job.JobInfo;
20 import android.app.job.JobScheduler;
21 import android.app.job.JobWorkItem;
22 import android.content.Intent;
23 import android.jobscheduler.MockJobService;
24 import android.os.PersistableBundle;
25 
26 import java.util.List;
27 
28 /**
29  * Tests related to created and reading JobWorkItem objects.
30  */
31 public class JobWorkItemTest extends BaseJobSchedulerTest {
32     private static final int JOB_ID = JobWorkItemTest.class.hashCode();
33     private static final Intent TEST_INTENT = new Intent("some.random.action");
34 
testAllInfoGivenToJob()35     public void testAllInfoGivenToJob() throws Exception {
36         final JobInfo jobInfo = new JobInfo.Builder(JOB_ID, kJobServiceComponent)
37                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
38                 .build();
39         final PersistableBundle pb = new PersistableBundle();
40         pb.putInt("random_key", 42);
41         final JobWorkItem expectedJwi = new JobWorkItem.Builder()
42                 .setIntent(TEST_INTENT)
43                 .setExtras(pb)
44                 .setEstimatedNetworkBytes(30, 20)
45                 .setMinimumNetworkChunkBytes(5)
46                 .build();
47         // JobWorkItem hasn't been scheduled yet. Delivery count should be 0.
48         assertEquals(0, expectedJwi.getDeliveryCount());
49 
50         try (NetworkingHelper networkingHelper =
51                      new NetworkingHelper(getInstrumentation(), getContext())) {
52             networkingHelper.setAllNetworksEnabled(true);
53             kTestEnvironment.setExpectedExecutions(1);
54             kTestEnvironment.setExpectedWork(new MockJobService.TestWorkItem[]{
55                     new MockJobService.TestWorkItem(TEST_INTENT)});
56             kTestEnvironment.readyToWork();
57             mJobScheduler.enqueue(jobInfo, expectedJwi);
58             runSatisfiedJob(JOB_ID);
59             assertTrue("Job didn't fire immediately", kTestEnvironment.awaitExecution());
60         }
61 
62         List<JobWorkItem> executedJwis = kTestEnvironment.getLastReceivedWork();
63         assertEquals(1, executedJwis.size());
64         final JobWorkItem actualJwi = executedJwis.get(0);
65         assertEquals(1, actualJwi.getDeliveryCount());
66         final Intent actualIntent = actualJwi.getIntent();
67         assertNotNull(actualIntent);
68         assertEquals(TEST_INTENT.getAction(), actualIntent.getAction());
69         final PersistableBundle extras = actualJwi.getExtras();
70         assertNotNull(extras);
71         assertEquals(1, extras.keySet().size());
72         assertEquals(42, extras.getInt("random_key"));
73         assertEquals(30, actualJwi.getEstimatedNetworkDownloadBytes());
74         assertEquals(20, actualJwi.getEstimatedNetworkUploadBytes());
75         assertEquals(5, actualJwi.getMinimumNetworkChunkBytes());
76     }
77 
testIntentOnlyItem_builder()78     public void testIntentOnlyItem_builder() {
79         JobWorkItem jwi = new JobWorkItem.Builder().setIntent(TEST_INTENT).build();
80 
81         assertEquals(TEST_INTENT, jwi.getIntent());
82         assertEquals(JobInfo.NETWORK_BYTES_UNKNOWN, jwi.getEstimatedNetworkDownloadBytes());
83         assertEquals(JobInfo.NETWORK_BYTES_UNKNOWN, jwi.getEstimatedNetworkUploadBytes());
84         assertEquals(JobInfo.NETWORK_BYTES_UNKNOWN, jwi.getMinimumNetworkChunkBytes());
85         // JobWorkItem hasn't been scheduled yet. Delivery count should be 0.
86         assertEquals(0, jwi.getDeliveryCount());
87         assertTrue(jwi.getExtras().isEmpty());
88     }
89 
testIntentOnlyItem_ctor()90     public void testIntentOnlyItem_ctor() {
91         JobWorkItem jwi = new JobWorkItem(TEST_INTENT);
92 
93         assertEquals(TEST_INTENT, jwi.getIntent());
94         assertEquals(JobInfo.NETWORK_BYTES_UNKNOWN, jwi.getEstimatedNetworkDownloadBytes());
95         assertEquals(JobInfo.NETWORK_BYTES_UNKNOWN, jwi.getEstimatedNetworkUploadBytes());
96         assertEquals(JobInfo.NETWORK_BYTES_UNKNOWN, jwi.getMinimumNetworkChunkBytes());
97         // JobWorkItem hasn't been scheduled yet. Delivery count should be 0.
98         assertEquals(0, jwi.getDeliveryCount());
99         assertTrue(jwi.getExtras().isEmpty());
100     }
101 
testItemWithEstimatedBytes_builder()102     public void testItemWithEstimatedBytes_builder() {
103         try {
104             new JobWorkItem.Builder().setEstimatedNetworkBytes(-10, 20).build();
105             fail("Successfully created JobWorkItem with negative download bytes value");
106         } catch (IllegalArgumentException expected) {
107             // Success
108         }
109 
110         try {
111             new JobWorkItem.Builder().setEstimatedNetworkBytes(10, -20).build();
112             fail("Successfully created JobWorkItem with negative upload bytes value");
113         } catch (IllegalArgumentException expected) {
114             // Success
115         }
116 
117         JobWorkItem jwi = new JobWorkItem.Builder().setEstimatedNetworkBytes(10, 20).build();
118         assertNull(jwi.getIntent());
119         assertEquals(10, jwi.getEstimatedNetworkDownloadBytes());
120         assertEquals(20, jwi.getEstimatedNetworkUploadBytes());
121         // JobWorkItem hasn't been scheduled yet. Delivery count should be 0.
122         assertEquals(0, jwi.getDeliveryCount());
123         assertTrue(jwi.getExtras().isEmpty());
124     }
125 
testItemWithEstimatedBytes_ctor()126     public void testItemWithEstimatedBytes_ctor() {
127         try {
128             new JobWorkItem(TEST_INTENT, -10, 20);
129             fail("Successfully created JobWorkItem with negative download bytes value");
130         } catch (IllegalArgumentException expected) {
131             // Success
132         }
133 
134         try {
135             new JobWorkItem(TEST_INTENT, 10, -20);
136             fail("Successfully created JobWorkItem with negative upload bytes value");
137         } catch (IllegalArgumentException expected) {
138             // Success
139         }
140 
141         JobWorkItem jwi = new JobWorkItem(TEST_INTENT, 10, 20);
142 
143         assertEquals(TEST_INTENT, jwi.getIntent());
144         assertEquals(10, jwi.getEstimatedNetworkDownloadBytes());
145         assertEquals(20, jwi.getEstimatedNetworkUploadBytes());
146         // JobWorkItem hasn't been scheduled yet. Delivery count should be 0.
147         assertEquals(0, jwi.getDeliveryCount());
148         assertTrue(jwi.getExtras().isEmpty());
149     }
150 
testItemWithMinimumChunkBytes_builder()151     public void testItemWithMinimumChunkBytes_builder() {
152         JobWorkItem jwi = new JobWorkItem.Builder().setMinimumNetworkChunkBytes(3).build();
153 
154         assertNull(jwi.getIntent());
155         assertEquals(JobInfo.NETWORK_BYTES_UNKNOWN, jwi.getEstimatedNetworkDownloadBytes());
156         assertEquals(JobInfo.NETWORK_BYTES_UNKNOWN, jwi.getEstimatedNetworkUploadBytes());
157         assertEquals(3, jwi.getMinimumNetworkChunkBytes());
158         // JobWorkItem hasn't been scheduled yet. Delivery count should be 0.
159         assertEquals(0, jwi.getDeliveryCount());
160         assertTrue(jwi.getExtras().isEmpty());
161 
162         try {
163             new JobWorkItem.Builder().setMinimumNetworkChunkBytes(-3).build();
164             fail("Successfully created JobWorkItem with negative minimum chunk value");
165         } catch (IllegalArgumentException expected) {
166             // Success
167         }
168         try {
169             new JobWorkItem.Builder().setMinimumNetworkChunkBytes(0).build();
170             fail("Successfully created JobWorkItem with 0 minimum chunk value");
171         } catch (IllegalArgumentException expected) {
172             // Success
173         }
174         try {
175             new JobWorkItem.Builder()
176                     .setEstimatedNetworkBytes(10, 20)
177                     .setMinimumNetworkChunkBytes(50)
178                     .build();
179             fail("Successfully created JobWorkItem with minimum chunk value too large");
180         } catch (IllegalArgumentException expected) {
181             // Success
182         }
183         try {
184             new JobWorkItem.Builder()
185                     .setEstimatedNetworkBytes(JobInfo.NETWORK_BYTES_UNKNOWN, 20)
186                     .setMinimumNetworkChunkBytes(25)
187                     .build();
188             fail("Successfully created JobWorkItem with minimum chunk value too large");
189         } catch (IllegalArgumentException expected) {
190             // Success
191         }
192         try {
193             new JobWorkItem.Builder()
194                     .setEstimatedNetworkBytes(10, JobInfo.NETWORK_BYTES_UNKNOWN)
195                     .setMinimumNetworkChunkBytes(15)
196                     .build();
197             fail("Successfully created JobWorkItem with minimum chunk value too large");
198         } catch (IllegalArgumentException expected) {
199             // Success
200         }
201     }
202 
testItemWithMinimumChunkBytes_ctor()203     public void testItemWithMinimumChunkBytes_ctor() {
204         JobWorkItem jwi = new JobWorkItem(TEST_INTENT, 10, 20, 3);
205 
206         assertEquals(TEST_INTENT, jwi.getIntent());
207         assertEquals(10, jwi.getEstimatedNetworkDownloadBytes());
208         assertEquals(20, jwi.getEstimatedNetworkUploadBytes());
209         assertEquals(3, jwi.getMinimumNetworkChunkBytes());
210         // JobWorkItem hasn't been scheduled yet. Delivery count should be 0.
211         assertEquals(0, jwi.getDeliveryCount());
212         assertTrue(jwi.getExtras().isEmpty());
213 
214         try {
215             new JobWorkItem(TEST_INTENT, 10, 20, -3);
216             fail("Successfully created JobWorkItem with negative minimum chunk value");
217         } catch (IllegalArgumentException expected) {
218             // Success
219         }
220         try {
221             new JobWorkItem(TEST_INTENT, 10, 20, 0);
222             fail("Successfully created JobWorkItem with 0 minimum chunk value");
223         } catch (IllegalArgumentException expected) {
224             // Success
225         }
226         try {
227             new JobWorkItem(TEST_INTENT, 10, 20, 50);
228             fail("Successfully created JobWorkItem with minimum chunk value too large");
229         } catch (IllegalArgumentException expected) {
230             // Success
231         }
232         try {
233             new JobWorkItem(TEST_INTENT, JobInfo.NETWORK_BYTES_UNKNOWN, 20, 25);
234             fail("Successfully created JobWorkItem with minimum chunk value too large");
235         } catch (IllegalArgumentException expected) {
236             // Success
237         }
238         try {
239             new JobWorkItem(TEST_INTENT, 10, JobInfo.NETWORK_BYTES_UNKNOWN, 15);
240             fail("Successfully created JobWorkItem with minimum chunk value too large");
241         } catch (IllegalArgumentException expected) {
242             // Success
243         }
244     }
245 
testItemWithPersistableBundle()246     public void testItemWithPersistableBundle() {
247         final PersistableBundle pb = new PersistableBundle();
248         pb.putInt("random_key", 42);
249         JobWorkItem jwi = new JobWorkItem.Builder().setExtras(pb).build();
250 
251         assertNull(jwi.getIntent());
252         assertEquals(JobInfo.NETWORK_BYTES_UNKNOWN, jwi.getEstimatedNetworkDownloadBytes());
253         assertEquals(JobInfo.NETWORK_BYTES_UNKNOWN, jwi.getEstimatedNetworkUploadBytes());
254         assertEquals(JobInfo.NETWORK_BYTES_UNKNOWN, jwi.getMinimumNetworkChunkBytes());
255         // JobWorkItem hasn't been scheduled yet. Delivery count should be 0.
256         assertEquals(0, jwi.getDeliveryCount());
257         final PersistableBundle extras = jwi.getExtras();
258         assertNotNull(extras);
259         assertEquals(1, extras.keySet().size());
260         assertEquals(42, extras.getInt("random_key"));
261 
262         try {
263             new JobWorkItem.Builder().setExtras(null).build();
264             fail("Successfully created null extras");
265         } catch (Exception expected) {
266             // Success
267         }
268     }
269 
testDeliveryCountBumped()270     public void testDeliveryCountBumped() throws Exception {
271         JobInfo jobInfo = new JobInfo.Builder(JOB_ID, kJobServiceComponent).build();
272         JobWorkItem jwi = new JobWorkItem(TEST_INTENT);
273         // JobWorkItem hasn't been scheduled yet. Delivery count should be 0.
274         assertEquals(0, jwi.getDeliveryCount());
275 
276         kTestEnvironment.setExpectedExecutions(1);
277         kTestEnvironment.setExpectedWork(new MockJobService.TestWorkItem[]{
278                 new MockJobService.TestWorkItem(TEST_INTENT)});
279         kTestEnvironment.readyToWork();
280         mJobScheduler.enqueue(jobInfo, jwi);
281         runSatisfiedJob(JOB_ID);
282         assertTrue("Job didn't fire immediately", kTestEnvironment.awaitExecution());
283 
284         List<JobWorkItem> executedJWIs = kTestEnvironment.getLastReceivedWork();
285         assertEquals(1, executedJWIs.size());
286         assertEquals(1, executedJWIs.get(0).getDeliveryCount());
287     }
288 
testPersisted_withIntent()289     public void testPersisted_withIntent() {
290         JobWorkItem jwi = new JobWorkItem.Builder().setIntent(TEST_INTENT).build();
291         JobInfo jobInfo = new JobInfo.Builder(JOB_ID, kJobServiceComponent)
292                 .setPersisted(true)
293                 .build();
294         try {
295             mJobScheduler.enqueue(jobInfo, jwi);
296             fail("Successfully enqueued persisted JWI with intent");
297         } catch (IllegalArgumentException expected) {
298             // Success
299         }
300     }
301 
testPersisted_withPersistableBundle()302     public void testPersisted_withPersistableBundle() {
303         final PersistableBundle pb = new PersistableBundle();
304         pb.putInt("random_key", 42);
305         JobWorkItem jwi = new JobWorkItem.Builder().setExtras(pb).build();
306         JobInfo jobInfo = new JobInfo.Builder(JOB_ID, kJobServiceComponent)
307                 .setPersisted(true)
308                 .build();
309 
310         assertEquals(JobScheduler.RESULT_SUCCESS, mJobScheduler.enqueue(jobInfo, jwi));
311     }
312 
testScheduleItemWithNetworkInfoAndNoNetworkConstraint_download()313     public void testScheduleItemWithNetworkInfoAndNoNetworkConstraint_download() {
314         JobWorkItem jwi = new JobWorkItem(TEST_INTENT, 10, JobInfo.NETWORK_BYTES_UNKNOWN);
315         JobInfo jobInfo = new JobInfo.Builder(JOB_ID, kJobServiceComponent).build();
316         try {
317             mJobScheduler.enqueue(jobInfo, jwi);
318             fail("Successfully scheduled JobWorkItem with network implication"
319                     + " and job with no network constraint");
320         } catch (IllegalArgumentException expected) {
321             // Success
322         }
323     }
324 
testScheduleItemWithNetworkInfoAndNoNetworkConstraint_upload()325     public void testScheduleItemWithNetworkInfoAndNoNetworkConstraint_upload() {
326         JobWorkItem jwi = new JobWorkItem(TEST_INTENT, JobInfo.NETWORK_BYTES_UNKNOWN, 10);
327         JobInfo jobInfo = new JobInfo.Builder(JOB_ID, kJobServiceComponent).build();
328         try {
329             mJobScheduler.enqueue(jobInfo, jwi);
330             fail("Successfully scheduled JobWorkItem with network implication"
331                     + " and job with no network constraint");
332         } catch (IllegalArgumentException expected) {
333             // Success
334         }
335     }
336 
testScheduleItemWithNetworkInfoAndNoNetworkConstraint_minimumChunk()337     public void testScheduleItemWithNetworkInfoAndNoNetworkConstraint_minimumChunk() {
338         JobWorkItem jwi = new JobWorkItem(TEST_INTENT,
339                 JobInfo.NETWORK_BYTES_UNKNOWN, JobInfo.NETWORK_BYTES_UNKNOWN, 10);
340         JobInfo jobInfo = new JobInfo.Builder(JOB_ID, kJobServiceComponent).build();
341         try {
342             mJobScheduler.enqueue(jobInfo, jwi);
343             fail("Successfully scheduled JobWorkItem with network implication"
344                     + " and job with no network constraint");
345         } catch (IllegalArgumentException expected) {
346             // Success
347         }
348     }
349 
testScheduleItemWithNetworkInfoAndNoNetworkConstraint()350     public void testScheduleItemWithNetworkInfoAndNoNetworkConstraint() {
351         JobWorkItem jwi = new JobWorkItem(TEST_INTENT, 10, 10, 10);
352         JobInfo jobInfo = new JobInfo.Builder(JOB_ID, kJobServiceComponent)
353                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
354                 .build();
355         try {
356             mJobScheduler.enqueue(jobInfo, jwi);
357             fail("Successfully scheduled JobWorkItem with network implication"
358                     + " and job with no network constraint");
359         } catch (IllegalArgumentException expected) {
360             // Success
361         }
362     }
363 
testScheduleItemWithNetworkInfoAndNetworkConstraint()364     public void testScheduleItemWithNetworkInfoAndNetworkConstraint() {
365         JobWorkItem jwi = new JobWorkItem(TEST_INTENT,
366                 JobInfo.NETWORK_BYTES_UNKNOWN, JobInfo.NETWORK_BYTES_UNKNOWN, 10);
367         JobInfo jobInfo = new JobInfo.Builder(JOB_ID, kJobServiceComponent)
368                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
369                 .build();
370         assertEquals(JobScheduler.RESULT_SUCCESS, mJobScheduler.enqueue(jobInfo, jwi));
371     }
372 }
373