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 
17 package android.jobscheduler;
18 
19 import android.annotation.NonNull;
20 import android.annotation.TargetApi;
21 import android.app.Notification;
22 import android.app.job.JobInfo;
23 import android.app.job.JobParameters;
24 import android.app.job.JobScheduler;
25 import android.app.job.JobService;
26 import android.app.job.JobWorkItem;
27 import android.content.ClipData;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.pm.PackageManager;
31 import android.net.Uri;
32 import android.os.Process;
33 import android.util.Log;
34 
35 import junit.framework.Assert;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.concurrent.CountDownLatch;
40 import java.util.concurrent.TimeUnit;
41 
42 /**
43  * Handles callback from the framework {@link android.app.job.JobScheduler}. The behaviour of this
44  * class is configured through the static
45  * {@link TestEnvironment}.
46  */
47 @TargetApi(21)
48 public class MockJobService extends JobService {
49     private static final String TAG = "MockJobService";
50 
51     /** Wait this long before timing out the test. */
52     private static final long DEFAULT_TIMEOUT_MILLIS = 30000L; // 30 seconds.
53 
54     private JobParameters mParams;
55 
56     ArrayList<JobWorkItem> mReceivedWork = new ArrayList<>();
57 
58     ArrayList<JobWorkItem> mPendingCompletions = new ArrayList<>();
59 
60     private boolean mWaitingForStop;
61 
62     private long mEstimatedDownloadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
63     private long mEstimatedUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
64     private long mTransferredDownloadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
65     private long mTransferredUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
66 
67     @Override
onDestroy()68     public void onDestroy() {
69         super.onDestroy();
70         Log.i(TAG, "Destroying test service");
71         if (TestEnvironment.getTestEnvironment().getExpectedWork() != null) {
72             TestEnvironment.getTestEnvironment().notifyExecution(this, mParams, 0, 0, mReceivedWork,
73                     null);
74         }
75     }
76 
77     @Override
onCreate()78     public void onCreate() {
79         super.onCreate();
80         Log.i(TAG, "Created test service.");
81     }
82 
83     @Override
onStartJob(JobParameters params)84     public boolean onStartJob(JobParameters params) {
85         Log.i(TAG, "Test job executing: " + params.getJobId());
86         mParams = params;
87         TestEnvironment.getTestEnvironment().addEvent(
88                 new TestEnvironment.Event(
89                         TestEnvironment.Event.EVENT_START_JOB, params.getJobId()));
90 
91         final Notification notificationToPost =
92                 TestEnvironment.getTestEnvironment().getJobStartNotification();
93         if (notificationToPost != null) {
94             setNotification(params,
95                     TestEnvironment.getTestEnvironment().getJobStartNotificationId(),
96                     notificationToPost,
97                     TestEnvironment.getTestEnvironment().getJobStartNotificationEndPolicy());
98         }
99         int permCheckRead = PackageManager.PERMISSION_DENIED;
100         int permCheckWrite = PackageManager.PERMISSION_DENIED;
101         ClipData clip = params.getClipData();
102         if (clip != null) {
103             permCheckRead = checkUriPermission(clip.getItemAt(0).getUri(), Process.myPid(),
104                     Process.myUid(), Intent.FLAG_GRANT_READ_URI_PERMISSION);
105             permCheckWrite = checkUriPermission(clip.getItemAt(0).getUri(), Process.myPid(),
106                     Process.myUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
107         }
108 
109         TestWorkItem[] expectedWork = TestEnvironment.getTestEnvironment().getExpectedWork();
110         if (expectedWork != null) {
111             try {
112                 if (!TestEnvironment.getTestEnvironment().awaitDoWork()) {
113                     TestEnvironment.getTestEnvironment().notifyExecution(this,
114                             params, permCheckRead,
115                             permCheckWrite, null, "Spent too long waiting to start executing work");
116                     return false;
117                 }
118             } catch (InterruptedException e) {
119                 TestEnvironment.getTestEnvironment().notifyExecution(this,
120                         params, permCheckRead,
121                         permCheckWrite, null, "Failed waiting for work: " + e);
122                 return false;
123             }
124             JobWorkItem work;
125             int index = 0;
126             while ((work = params.dequeueWork()) != null) {
127                 final Intent intent = work.getIntent();
128                 Log.i(TAG, "Received work #" + index + ": " + intent);
129                 mReceivedWork.add(work);
130 
131                 int flags = 0;
132 
133                 if (index < expectedWork.length) {
134                     TestWorkItem expected = expectedWork[index];
135                     int grantFlags = intent == null ? 0 : intent.getFlags();
136                     if (expected.requireUrisGranted != null) {
137                         for (int ui = 0; ui < expected.requireUrisGranted.length; ui++) {
138                             if ((grantFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
139                                 if (checkUriPermission(expected.requireUrisGranted[ui],
140                                         Process.myPid(), Process.myUid(),
141                                         Intent.FLAG_GRANT_READ_URI_PERMISSION)
142                                         != PackageManager.PERMISSION_GRANTED) {
143                                     TestEnvironment.getTestEnvironment().notifyExecution(this,
144                                             params,
145                                             permCheckRead, permCheckWrite, null,
146                                             "Expected read permission but not granted: "
147                                                     + expected.requireUrisGranted[ui]
148                                                     + " @ #" + index);
149                                     return false;
150                                 }
151                             }
152                             if ((grantFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
153                                 if (checkUriPermission(expected.requireUrisGranted[ui],
154                                         Process.myPid(), Process.myUid(),
155                                         Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
156                                         != PackageManager.PERMISSION_GRANTED) {
157                                     TestEnvironment.getTestEnvironment().notifyExecution(this,
158                                             params,
159                                             permCheckRead, permCheckWrite, null,
160                                             "Expected write permission but not granted: "
161                                                     + expected.requireUrisGranted[ui]
162                                                     + " @ #" + index);
163                                     return false;
164                                 }
165                             }
166                         }
167                     }
168                     if (expected.requireUrisNotGranted != null) {
169                         // XXX note no delay here, current impl will have fully revoked the
170                         // permission by the time we return from completing the last work.
171                         for (int ui = 0; ui < expected.requireUrisNotGranted.length; ui++) {
172                             if ((grantFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
173                                 if (checkUriPermission(expected.requireUrisNotGranted[ui],
174                                         Process.myPid(), Process.myUid(),
175                                         Intent.FLAG_GRANT_READ_URI_PERMISSION)
176                                         != PackageManager.PERMISSION_DENIED) {
177                                     TestEnvironment.getTestEnvironment().notifyExecution(this,
178                                             params,
179                                             permCheckRead, permCheckWrite, null,
180                                             "Not expected read permission but granted: "
181                                                     + expected.requireUrisNotGranted[ui]
182                                                     + " @ #" + index);
183                                     return false;
184                                 }
185                             }
186                             if ((grantFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
187                                 if (checkUriPermission(expected.requireUrisNotGranted[ui],
188                                         Process.myPid(), Process.myUid(),
189                                         Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
190                                         != PackageManager.PERMISSION_DENIED) {
191                                     TestEnvironment.getTestEnvironment().notifyExecution(this,
192                                             params,
193                                             permCheckRead, permCheckWrite, null,
194                                             "Not expected write permission but granted: "
195                                                     + expected.requireUrisNotGranted[ui]
196                                                     + " @ #" + index);
197                                     return false;
198                                 }
199                             }
200                         }
201                     }
202 
203                     flags = expected.flags;
204 
205                     if ((flags & TestWorkItem.FLAG_WAIT_FOR_STOP) != 0) {
206                         Log.i(TAG, "Now waiting to stop");
207                         mWaitingForStop = true;
208                         TestEnvironment.getTestEnvironment().notifyWaitingForStop();
209                         return true;
210                     }
211 
212                     if ((flags & TestWorkItem.FLAG_COMPLETE_NEXT) != 0) {
213                         if (!processNextPendingCompletion()) {
214                             TestEnvironment.getTestEnvironment().notifyExecution(this, params,
215                                     0, 0, null,
216                                     "Expected to complete next pending work but there was none: "
217                                             + " @ #" + index);
218                             return false;
219                         }
220                     }
221                 }
222 
223                 if ((flags & TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK) != 0) {
224                     mPendingCompletions.add(work);
225                 } else if ((flags & TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_TOP) != 0) {
226                     mPendingCompletions.add(0, work);
227                 } else {
228                     mParams.completeWork(work);
229                 }
230 
231                 if (index < expectedWork.length) {
232                     TestWorkItem expected = expectedWork[index];
233                     if (expected.subitems != null) {
234                         final TestWorkItem[] sub = expected.subitems;
235                         final JobInfo ji = expected.jobInfo;
236                         final JobScheduler js = (JobScheduler) getSystemService(
237                                 Context.JOB_SCHEDULER_SERVICE);
238                         for (int subi = 0; subi < sub.length; subi++) {
239                             js.enqueue(ji, new JobWorkItem(sub[subi].intent));
240                         }
241                     }
242                 }
243 
244                 index++;
245             }
246 
247             if (processNextPendingCompletion()) {
248                 // We had some pending completions, clean them all out...
249                 while (processNextPendingCompletion()) {
250                 }
251                 // ...and we need to do a final dequeue to complete the job, which should not
252                 // return any remaining work.
253                 if ((work = params.dequeueWork()) != null) {
254                     TestEnvironment.getTestEnvironment().notifyExecution(this, params,
255                             0, 0, null,
256                             "Expected no remaining work after dequeue pending, but got: " + work);
257                 }
258             }
259 
260             Log.i(TAG, "Done with all work at #" + index);
261             // We don't notifyExecution here because we want to make sure the job properly
262             // stops itself.
263             return true;
264         } else {
265             boolean continueAfterStart
266                     = TestEnvironment.getTestEnvironment().handleContinueAfterStart();
267             try {
268                 if (!TestEnvironment.getTestEnvironment().awaitDoJob()) {
269                     TestEnvironment.getTestEnvironment().notifyExecution(this,
270                             params, permCheckRead,
271                             permCheckWrite, null, "Spent too long waiting to start job");
272                     return false;
273                 }
274             } catch (InterruptedException e) {
275                 TestEnvironment.getTestEnvironment().notifyExecution(this, params, permCheckRead,
276                         permCheckWrite, null, "Failed waiting to start job: " + e);
277                 return false;
278             }
279             TestEnvironment.getTestEnvironment().notifyExecution(this, params, permCheckRead,
280                     permCheckWrite, null, null);
281             return continueAfterStart;
282         }
283     }
284 
285     @Override
onNetworkChanged(JobParameters params)286     public void onNetworkChanged(JobParameters params) {
287         TestEnvironment.getTestEnvironment().notifyNetworkChanged(params);
288     }
289 
processNextPendingCompletion()290     boolean processNextPendingCompletion() {
291         if (mPendingCompletions.size() <= 0) {
292             return false;
293         }
294 
295         JobWorkItem next = mPendingCompletions.remove(0);
296         mParams.completeWork(next);
297         return true;
298     }
299 
300     @Override
onStopJob(JobParameters params)301     public boolean onStopJob(JobParameters params) {
302         Log.i(TAG, "Received stop callback");
303         TestEnvironment.getTestEnvironment().notifyStopped(params);
304         return mWaitingForStop || TestEnvironment.getTestEnvironment().requestReschedule();
305     }
306 
307     @Override
getTransferredDownloadBytes(@onNull JobParameters params)308     public long getTransferredDownloadBytes(@NonNull JobParameters params) {
309         return mTransferredDownloadBytes;
310     }
311 
312     @Override
getTransferredUploadBytes(@onNull JobParameters params)313     public long getTransferredUploadBytes(@NonNull JobParameters params) {
314         return mTransferredUploadBytes;
315     }
316 
317     @Override
getTransferredDownloadBytes(@onNull JobParameters params, @NonNull JobWorkItem item)318     public long getTransferredDownloadBytes(@NonNull JobParameters params,
319             @NonNull JobWorkItem item) {
320         return mTransferredDownloadBytes;
321     }
322 
323     @Override
getTransferredUploadBytes(@onNull JobParameters params, @NonNull JobWorkItem item)324     public long getTransferredUploadBytes(@NonNull JobParameters params,
325             @NonNull JobWorkItem item) {
326         return mTransferredUploadBytes;
327     }
328 
setEstimatedNetworkBytesForTest(long downloadBytes, long uploadBytes)329     private void setEstimatedNetworkBytesForTest(long downloadBytes, long uploadBytes) {
330         mEstimatedDownloadBytes = downloadBytes;
331         mEstimatedUploadBytes = uploadBytes;
332         updateEstimatedNetworkBytes(mParams, downloadBytes, uploadBytes);
333     }
334 
setTransferredBytesForTest(long downloadBytes, long uploadBytes)335     private void setTransferredBytesForTest(long downloadBytes, long uploadBytes) {
336         mTransferredDownloadBytes = downloadBytes;
337         mTransferredUploadBytes = uploadBytes;
338         updateTransferredNetworkBytes(mParams, downloadBytes, uploadBytes);
339     }
340 
341     public static final class TestWorkItem {
342         /**
343          * Stop processing work for now, waiting for the service to be stopped.
344          */
345         public static final int FLAG_WAIT_FOR_STOP = 1<<0;
346         /**
347          * Don't complete this work now, instead push it on the back of the stack of
348          * pending completions.
349          */
350         public static final int FLAG_DELAY_COMPLETE_PUSH_BACK = 1<<1;
351         /**
352          * Don't complete this work now, instead insert to the top of the stack of
353          * pending completions.
354          */
355         public static final int FLAG_DELAY_COMPLETE_PUSH_TOP = 1<<2;
356         /**
357          * Complete next pending completion on the stack before completing this one.
358          */
359         public static final int FLAG_COMPLETE_NEXT = 1<<3;
360 
361         public final Intent intent;
362         public final JobInfo jobInfo;
363         public final int flags;
364         public final int deliveryCount;
365         public final TestWorkItem[] subitems;
366         public final Uri[] requireUrisGranted;
367         public final Uri[] requireUrisNotGranted;
368 
TestWorkItem(Intent _intent)369         public TestWorkItem(Intent _intent) {
370             intent = _intent;
371             jobInfo = null;
372             flags = 0;
373             deliveryCount = 1;
374             subitems = null;
375             requireUrisGranted = null;
376             requireUrisNotGranted = null;
377         }
378 
TestWorkItem(Intent _intent, int _flags)379         public TestWorkItem(Intent _intent, int _flags) {
380             intent = _intent;
381             jobInfo = null;
382             flags = _flags;
383             deliveryCount = 1;
384             subitems = null;
385             requireUrisGranted = null;
386             requireUrisNotGranted = null;
387         }
388 
TestWorkItem(Intent _intent, int _flags, int _deliveryCount)389         public TestWorkItem(Intent _intent, int _flags, int _deliveryCount) {
390             intent = _intent;
391             jobInfo = null;
392             flags = _flags;
393             deliveryCount = _deliveryCount;
394             subitems = null;
395             requireUrisGranted = null;
396             requireUrisNotGranted = null;
397         }
398 
TestWorkItem(Intent _intent, JobInfo _jobInfo, TestWorkItem[] _subitems)399         public TestWorkItem(Intent _intent, JobInfo _jobInfo, TestWorkItem[] _subitems) {
400             intent = _intent;
401             jobInfo = _jobInfo;
402             flags = 0;
403             deliveryCount = 1;
404             subitems = _subitems;
405             requireUrisGranted = null;
406             requireUrisNotGranted = null;
407         }
408 
TestWorkItem(Intent _intent, Uri[] _requireUrisGranted, Uri[] _requireUrisNotGranted)409         public TestWorkItem(Intent _intent, Uri[] _requireUrisGranted,
410                 Uri[] _requireUrisNotGranted) {
411             intent = _intent;
412             jobInfo = null;
413             flags = 0;
414             deliveryCount = 1;
415             subitems = null;
416             requireUrisGranted = _requireUrisGranted;
417             requireUrisNotGranted = _requireUrisNotGranted;
418         }
419 
420         @Override
toString()421         public String toString() {
422             return "TestWorkItem { " + intent + " dc=" + deliveryCount + " }";
423         }
424     }
425 
426     /**
427      * Configures the expected behaviour for each test. This object is shared across consecutive
428      * tests, so to clear state each test is responsible for calling
429      * {@link TestEnvironment#setUp()}.
430      */
431     public static final class TestEnvironment {
432 
433         private static TestEnvironment kTestEnvironment;
434         //public static final int INVALID_JOB_ID = -1;
435 
436         private CountDownLatch mLatch;
437         private CountDownLatch mWaitingForStopLatch;
438         private CountDownLatch mDoJobLatch;
439         private CountDownLatch mStoppedLatch;
440         private CountDownLatch mDoWorkLatch;
441         private CountDownLatch mNetworkChangeLatch;
442         private TestWorkItem[] mExpectedWork;
443         private boolean mContinueAfterStart;
444         private boolean mRequestReschedule;
445         private JobParameters mExecutedJobParameters;
446         private JobParameters mNetworkChangedJobParameters;
447         private MockJobService mExecutedJobService;
448         private int mExecutedPermCheckRead;
449         private int mExecutedPermCheckWrite;
450         private ArrayList<JobWorkItem> mExecutedReceivedWork;
451         private String mExecutedErrorMessage;
452         private JobParameters mStopJobParameters;
453         private List<Event> mExecutedEvents = new ArrayList<>();
454         private int mJobStartNotificationId;
455         private Notification mJobStartNotification;
456         private int mJobStartNotificationEndPolicy;
457 
getTestEnvironment()458         public static TestEnvironment getTestEnvironment() {
459             if (kTestEnvironment == null) {
460                 kTestEnvironment = new TestEnvironment();
461             }
462             return kTestEnvironment;
463         }
464 
getExpectedWork()465         public TestWorkItem[] getExpectedWork() {
466             return mExpectedWork;
467         }
468 
getJobStartNotification()469         private Notification getJobStartNotification() {
470             return mJobStartNotification;
471         }
472 
getJobStartNotificationEndPolicy()473         private int getJobStartNotificationEndPolicy() {
474             return mJobStartNotificationEndPolicy;
475         }
476 
getJobStartNotificationId()477         private int getJobStartNotificationId() {
478             return mJobStartNotificationId;
479         }
480 
getLastStartJobParameters()481         public JobParameters getLastStartJobParameters() {
482             return mExecutedJobParameters;
483         }
484 
getLastStopJobParameters()485         public JobParameters getLastStopJobParameters() {
486             return mStopJobParameters;
487         }
488 
getLastNetworkChangedJobParameters()489         public JobParameters getLastNetworkChangedJobParameters() {
490             return mNetworkChangedJobParameters;
491         }
492 
getLastPermCheckRead()493         public int getLastPermCheckRead() {
494             return mExecutedPermCheckRead;
495         }
496 
getLastPermCheckWrite()497         public int getLastPermCheckWrite() {
498             return mExecutedPermCheckWrite;
499         }
500 
getLastReceivedWork()501         public ArrayList<JobWorkItem> getLastReceivedWork() {
502             return mExecutedReceivedWork;
503         }
504 
getLastErrorMessage()505         public String getLastErrorMessage() {
506             return mExecutedErrorMessage;
507         }
508 
509         /**
510          * Block the test thread, waiting on the JobScheduler to execute some previously scheduled
511          * job on this service.
512          */
awaitExecution()513         public boolean awaitExecution() throws InterruptedException {
514             return awaitExecution(DEFAULT_TIMEOUT_MILLIS);
515         }
516 
awaitExecution(long timeoutMillis)517         public boolean awaitExecution(long timeoutMillis) throws InterruptedException {
518             final boolean executed = mLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
519             if (getLastErrorMessage() != null) {
520                 Assert.fail(getLastErrorMessage());
521             }
522             return executed;
523         }
524 
525         /**
526          * Block the test thread, expecting to timeout but still listening to ensure that no jobs
527          * land in the interim.
528          * @return True if the latch timed out waiting on an execution.
529          */
awaitTimeout()530         public boolean awaitTimeout() throws InterruptedException {
531             return awaitTimeout(DEFAULT_TIMEOUT_MILLIS);
532         }
533 
awaitTimeout(long timeoutMillis)534         public boolean awaitTimeout(long timeoutMillis) throws InterruptedException {
535             return !mLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
536         }
537 
awaitWaitingForStop()538         public boolean awaitWaitingForStop() throws InterruptedException {
539             return mWaitingForStopLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
540         }
541 
awaitDoWork()542         public boolean awaitDoWork() throws InterruptedException {
543             return mDoWorkLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
544         }
545 
awaitDoJob()546         public boolean awaitDoJob() throws InterruptedException {
547             if (mDoJobLatch == null) {
548                 return true;
549             }
550             return mDoJobLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
551         }
552 
awaitNetworkChange()553         public boolean awaitNetworkChange() throws InterruptedException {
554             return mNetworkChangeLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
555         }
556 
awaitStopped()557         public boolean awaitStopped() throws InterruptedException {
558             return mStoppedLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
559         }
560 
notifyExecution(MockJobService jobService, JobParameters params, int permCheckRead, int permCheckWrite, ArrayList<JobWorkItem> receivedWork, String errorMsg)561         private void notifyExecution(MockJobService jobService, JobParameters params,
562                 int permCheckRead, int permCheckWrite,
563                 ArrayList<JobWorkItem> receivedWork, String errorMsg) {
564             mExecutedJobService = jobService;
565             mExecutedJobParameters = params;
566             mExecutedPermCheckRead = permCheckRead;
567             mExecutedPermCheckWrite = permCheckWrite;
568             mExecutedReceivedWork = receivedWork;
569             mExecutedErrorMessage = errorMsg;
570             if (mLatch != null) {
571                 mLatch.countDown();
572             }
573         }
574 
notifyNetworkChanged(JobParameters params)575         private void notifyNetworkChanged(JobParameters params) {
576             mNetworkChangedJobParameters = params;
577             if (mNetworkChangeLatch != null) {
578                 mNetworkChangeLatch.countDown();
579             }
580         }
581 
notifyWaitingForStop()582         private void notifyWaitingForStop() {
583             mWaitingForStopLatch.countDown();
584         }
585 
notifyStopped(JobParameters params)586         private void notifyStopped(JobParameters params) {
587             mStopJobParameters = params;
588             if (mStoppedLatch != null) {
589                 mStoppedLatch.countDown();
590             }
591         }
592 
setEstimatedNetworkBytes(long downloadBytes, long uploadBytes)593         public void setEstimatedNetworkBytes(long downloadBytes, long uploadBytes) {
594             mExecutedJobService.setEstimatedNetworkBytesForTest(downloadBytes, uploadBytes);
595         }
596 
setTransferredNetworkBytes(long downloadBytes, long uploadBytes)597         public void setTransferredNetworkBytes(long downloadBytes, long uploadBytes) {
598             mExecutedJobService.setTransferredBytesForTest(downloadBytes, uploadBytes);
599         }
600 
setExpectedExecutions(int numExecutions)601         public void setExpectedExecutions(int numExecutions) {
602             // For no executions expected, set count to 1 so we can still block for the timeout.
603             if (numExecutions == 0) {
604                 mLatch = new CountDownLatch(1);
605             } else {
606                 mLatch = new CountDownLatch(numExecutions);
607             }
608             mWaitingForStopLatch = null;
609             mDoJobLatch = null;
610             mStoppedLatch = null;
611             mDoWorkLatch = null;
612             mNetworkChangeLatch = null;
613             mExpectedWork = null;
614             mContinueAfterStart = false;
615             mRequestReschedule = false;
616             mExecutedEvents.clear();
617             mJobStartNotification = null;
618         }
619 
setExpectedWaitForStop()620         public void setExpectedWaitForStop() {
621             mWaitingForStopLatch = new CountDownLatch(1);
622         }
623 
setExpectedWork(TestWorkItem[] work)624         public void setExpectedWork(TestWorkItem[] work) {
625             mExpectedWork = work;
626             mDoWorkLatch = new CountDownLatch(1);
627         }
628 
setExpectedStopped()629         public void setExpectedStopped() {
630             mStoppedLatch = new CountDownLatch(1);
631         }
632 
setExpectedNetworkChange()633         public void setExpectedNetworkChange() {
634             mNetworkChangeLatch = new CountDownLatch(1);
635         }
636 
setNotificationAtStart(int notificationId, @NonNull Notification notification, @JobEndNotificationPolicy int jobEndNotificationPolicy)637         public void setNotificationAtStart(int notificationId,
638                 @NonNull Notification notification,
639                 @JobEndNotificationPolicy int jobEndNotificationPolicy) {
640             mJobStartNotificationId = notificationId;
641             mJobStartNotification = notification;
642             mJobStartNotificationEndPolicy = jobEndNotificationPolicy;
643         }
644 
readyToWork()645         public void readyToWork() {
646             mDoWorkLatch.countDown();
647         }
648 
setExpectedWaitForRun()649         public void setExpectedWaitForRun() {
650             mDoJobLatch = new CountDownLatch(1);
651         }
652 
readyToRun()653         public void readyToRun() {
654             mDoJobLatch.countDown();
655         }
656 
setContinueAfterStart()657         public void setContinueAfterStart() {
658             mContinueAfterStart = true;
659         }
660 
handleContinueAfterStart()661         public boolean handleContinueAfterStart() {
662             boolean res = mContinueAfterStart;
663             mContinueAfterStart = false;
664             return res;
665         }
666 
setRequestReschedule()667         public void setRequestReschedule() {
668             mRequestReschedule = true;
669         }
670 
requestReschedule()671         boolean requestReschedule() {
672             return mRequestReschedule;
673         }
674 
675         /** Called in each testCase#setup */
setUp()676         public void setUp() {
677             mLatch = null;
678             mExecutedJobParameters = null;
679             mExecutedJobService = null;
680             mStopJobParameters = null;
681         }
682 
addEvent(Event event)683         void addEvent(Event event) {
684             mExecutedEvents.add(event);
685         }
686 
getExecutedEvents()687         public List<Event> getExecutedEvents() {
688             return mExecutedEvents;
689         }
690 
691         public static class Event {
692             public static final int EVENT_START_JOB = 0;
693 
694             public int event;
695             public int jobId;
696 
Event(int event, int jobId)697             public Event(int event, int jobId) {
698                 this.event = event;
699                 this.jobId = jobId;
700             }
701 
702             @Override
equals(Object other)703             public boolean equals(Object other) {
704                 if (this == other) {
705                     return true;
706                 }
707                 if (other instanceof Event) {
708                     Event otherEvent = (Event) other;
709                     return otherEvent.event == event && otherEvent.jobId == jobId;
710                 }
711                 return false;
712             }
713 
714             @Override
hashCode()715             public int hashCode() {
716                 return event + 31 * jobId;
717             }
718 
719             @Override
toString()720             public String toString() {
721                 return "Event{" + event + ", " + jobId + "}";
722             }
723         }
724     }
725 }
726