101ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williamspackage com.android.server.job;
23d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
3d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
43ec36ef2af10558a0a94aa56331ce16bc6b18914Pavel Maltsevimport static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
5d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport static android.net.NetworkCapabilities.TRANSPORT_WIFI;
6d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
7d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport static org.junit.Assert.assertEquals;
8d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport static org.junit.Assert.assertTrue;
9d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport static org.junit.Assert.fail;
109252b34065809731ea2f6d3ffad91f678f809c93Jeff Sharkeyimport static org.mockito.ArgumentMatchers.anyString;
119252b34065809731ea2f6d3ffad91f678f809c93Jeff Sharkeyimport static org.mockito.Mockito.mock;
129252b34065809731ea2f6d3ffad91f678f809c93Jeff Sharkeyimport static org.mockito.Mockito.when;
133d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
147060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport android.app.job.JobInfo;
157060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport android.app.job.JobInfo.Builder;
16d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport android.content.ComponentName;
17d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport android.content.Context;
189252b34065809731ea2f6d3ffad91f678f809c93Jeff Sharkeyimport android.content.pm.PackageManagerInternal;
19d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport android.net.NetworkRequest;
209252b34065809731ea2f6d3ffad91f678f809c93Jeff Sharkeyimport android.os.Build;
21d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport android.os.Parcel;
22d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport android.os.Parcelable;
233d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williamsimport android.os.PersistableBundle;
24fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williamsimport android.os.SystemClock;
25d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport android.support.test.InstrumentationRegistry;
26d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport android.support.test.runner.AndroidJUnit4;
273d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williamsimport android.test.RenamingDelegatingContext;
283d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williamsimport android.util.Log;
29616541d7016ee4440051738a8f0c7c3b7086d375Christopher Tateimport android.util.Pair;
303d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
31d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport com.android.internal.util.HexDump;
32d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport com.android.server.IoThread;
339252b34065809731ea2f6d3ffad91f678f809c93Jeff Sharkeyimport com.android.server.LocalServices;
342f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tateimport com.android.server.job.JobStore.JobSet;
357060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport com.android.server.job.controllers.JobStatus;
363d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
37d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport org.junit.After;
38d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport org.junit.Before;
39d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport org.junit.Test;
40d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport org.junit.runner.RunWith;
41d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
42d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport java.time.Clock;
43d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport java.time.ZoneOffset;
44d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport java.util.Arrays;
4501ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williamsimport java.util.Iterator;
46d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport java.util.concurrent.CountDownLatch;
47d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeyimport java.util.concurrent.TimeUnit;
483d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
493d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams/**
503d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams * Test reading and writing correctly from file.
5115407846682b269e6b7fd0c24b84f709257fab5dMakoto Onuki *
5215407846682b269e6b7fd0c24b84f709257fab5dMakoto Onuki * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
533d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams */
54d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey@RunWith(AndroidJUnit4.class)
55d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkeypublic class JobStoreTest {
563d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    private static final String TAG = "TaskStoreTest";
573d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    private static final String TEST_PREFIX = "_test_";
5801ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams
59d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    private static final int SOME_UID = android.os.Process.FIRST_APPLICATION_UID;
603d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    private ComponentName mComponent;
613d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
627060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    JobStore mTaskStoreUnderTest;
633d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    Context mTestContext;
643d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
65d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    private Context getContext() {
66d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        return InstrumentationRegistry.getContext();
67d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    }
68d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
69d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    @Before
703d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    public void setUp() throws Exception {
713d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mTestContext = new RenamingDelegatingContext(getContext(), TEST_PREFIX);
723d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        Log.d(TAG, "Saving tasks to '" + mTestContext.getFilesDir() + "'");
7301ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        mTaskStoreUnderTest =
7401ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams                JobStore.initAndGetForTesting(mTestContext, mTestContext.getFilesDir());
753d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mComponent = new ComponentName(getContext().getPackageName(), StubClass.class.getName());
76d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
779252b34065809731ea2f6d3ffad91f678f809c93Jeff Sharkey        // Assume all packages are current SDK
789252b34065809731ea2f6d3ffad91f678f809c93Jeff Sharkey        final PackageManagerInternal pm = mock(PackageManagerInternal.class);
799252b34065809731ea2f6d3ffad91f678f809c93Jeff Sharkey        when(pm.getPackageTargetSdkVersion(anyString()))
809252b34065809731ea2f6d3ffad91f678f809c93Jeff Sharkey                .thenReturn(Build.VERSION_CODES.CUR_DEVELOPMENT);
819252b34065809731ea2f6d3ffad91f678f809c93Jeff Sharkey        LocalServices.removeServiceForTest(PackageManagerInternal.class);
829252b34065809731ea2f6d3ffad91f678f809c93Jeff Sharkey        LocalServices.addService(PackageManagerInternal.class, pm);
839252b34065809731ea2f6d3ffad91f678f809c93Jeff Sharkey
84d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        // Freeze the clocks at this moment in time
85d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        JobSchedulerService.sSystemClock =
86d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
87d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        JobSchedulerService.sUptimeMillisClock =
88d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
89d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        JobSchedulerService.sElapsedRealtimeClock =
90d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
913d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
923d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
93d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    @After
943d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    public void tearDown() throws Exception {
953d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mTaskStoreUnderTest.clear();
963d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
973d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
98d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    private void waitForPendingIo() throws Exception {
99d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        final CountDownLatch latch = new CountDownLatch(1);
100d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        IoThread.getHandler().post(() -> {
101d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey            latch.countDown();
102d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        });
103d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        latch.await(10, TimeUnit.SECONDS);
104d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    }
105d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
106d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    @Test
1073d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    public void testMaybeWriteStatusToDisk() throws Exception {
1083d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        int taskId = 5;
1093d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        long runByMillis = 20000L; // 20s
1103d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        long runFromMillis = 2000L; // 2s
1113d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        long initialBackoff = 10000L; // 10s
1123d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
1137060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        final JobInfo task = new Builder(taskId, mComponent)
1143d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setRequiresCharging(true)
115d1c06753d045ad10e00e7aba53ee2adba0712cccMatthew Williams                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
116d1c06753d045ad10e00e7aba53ee2adba0712cccMatthew Williams                .setBackoffCriteria(initialBackoff, JobInfo.BACKOFF_POLICY_EXPONENTIAL)
1173d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setOverrideDeadline(runByMillis)
1183d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setMinimumLatency(runFromMillis)
119d1c06753d045ad10e00e7aba53ee2adba0712cccMatthew Williams                .setPersisted(true)
1203d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .build();
1211085ff6ee531931ef7f55cbadbc83616f619b292Dianne Hackborn        final JobStatus ts = JobStatus.createFromJobInfo(task, SOME_UID, null, -1, null);
12215407846682b269e6b7fd0c24b84f709257fab5dMakoto Onuki        ts.addInternalFlags(JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION);
1233d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mTaskStoreUnderTest.add(ts);
124d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        waitForPendingIo();
125d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
1263d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        // Manually load tasks from xml file.
1272f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        final JobSet jobStatusSet = new JobSet();
128616541d7016ee4440051738a8f0c7c3b7086d375Christopher Tate        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
12901ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams
13001ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        assertEquals("Didn't get expected number of persisted tasks.", 1, jobStatusSet.size());
1312f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        final JobStatus loadedTaskStatus = jobStatusSet.getAllJobs().get(0);
13201ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        assertTasksEqual(task, loadedTaskStatus.getJob());
13348a30db75dd0eedf8e065c89825b2af86a381b62Matthew Williams        assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(ts));
13401ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        assertEquals("Different uids.", SOME_UID, loadedTaskStatus.getUid());
13515407846682b269e6b7fd0c24b84f709257fab5dMakoto Onuki        assertEquals(JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION,
13615407846682b269e6b7fd0c24b84f709257fab5dMakoto Onuki                loadedTaskStatus.getInternalFlags());
13701ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
13801ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams                ts.getEarliestRunTime(), loadedTaskStatus.getEarliestRunTime());
13901ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
14001ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams                ts.getLatestRunTimeElapsed(), loadedTaskStatus.getLatestRunTimeElapsed());
1413d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
1423d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
143d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    @Test
1443d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    public void testWritingTwoFilesToDisk() throws Exception {
1457060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        final JobInfo task1 = new Builder(8, mComponent)
1463d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setRequiresDeviceIdle(true)
1473d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setPeriodic(10000L)
1483d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setRequiresCharging(true)
149d1c06753d045ad10e00e7aba53ee2adba0712cccMatthew Williams                .setPersisted(true)
1503d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .build();
1517060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        final JobInfo task2 = new Builder(12, mComponent)
1523d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setMinimumLatency(5000L)
153d1c06753d045ad10e00e7aba53ee2adba0712cccMatthew Williams                .setBackoffCriteria(15000L, JobInfo.BACKOFF_POLICY_LINEAR)
1543d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setOverrideDeadline(30000L)
155d1c06753d045ad10e00e7aba53ee2adba0712cccMatthew Williams                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
156d1c06753d045ad10e00e7aba53ee2adba0712cccMatthew Williams                .setPersisted(true)
1573d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .build();
1581085ff6ee531931ef7f55cbadbc83616f619b292Dianne Hackborn        final JobStatus taskStatus1 = JobStatus.createFromJobInfo(task1, SOME_UID, null, -1, null);
1591085ff6ee531931ef7f55cbadbc83616f619b292Dianne Hackborn        final JobStatus taskStatus2 = JobStatus.createFromJobInfo(task2, SOME_UID, null, -1, null);
1603d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mTaskStoreUnderTest.add(taskStatus1);
1613d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mTaskStoreUnderTest.add(taskStatus2);
162d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        waitForPendingIo();
16301ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams
1642f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        final JobSet jobStatusSet = new JobSet();
165616541d7016ee4440051738a8f0c7c3b7086d375Christopher Tate        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
16601ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        assertEquals("Incorrect # of persisted tasks.", 2, jobStatusSet.size());
1672f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        Iterator<JobStatus> it = jobStatusSet.getAllJobs().iterator();
16801ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        JobStatus loaded1 = it.next();
16901ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        JobStatus loaded2 = it.next();
170fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams
171fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams        // Reverse them so we know which comparison to make.
172fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams        if (loaded1.getJobId() != 8) {
173fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams            JobStatus tmp = loaded1;
174fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams            loaded1 = loaded2;
175fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams            loaded2 = tmp;
176fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams        }
177fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams
17801ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        assertTasksEqual(task1, loaded1.getJob());
17901ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        assertTasksEqual(task2, loaded2.getJob());
18048a30db75dd0eedf8e065c89825b2af86a381b62Matthew Williams        assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(taskStatus1));
18148a30db75dd0eedf8e065c89825b2af86a381b62Matthew Williams        assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(taskStatus2));
18201ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        // Check that the loaded task has the correct runtimes.
18301ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
18401ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams                taskStatus1.getEarliestRunTime(), loaded1.getEarliestRunTime());
18501ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
18601ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams                taskStatus1.getLatestRunTimeElapsed(), loaded1.getLatestRunTimeElapsed());
18701ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
18801ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams                taskStatus2.getEarliestRunTime(), loaded2.getEarliestRunTime());
18901ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
19001ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams                taskStatus2.getLatestRunTimeElapsed(), loaded2.getLatestRunTimeElapsed());
1913d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
1923d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
193d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    @Test
1943d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    public void testWritingTaskWithExtras() throws Exception {
1957060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        JobInfo.Builder b = new Builder(8, mComponent)
1963d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setRequiresDeviceIdle(true)
1973d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setPeriodic(10000L)
198900c67fc51fc2672458dd1c9641250f2ecc01a31Matthew Williams                .setRequiresCharging(true)
199d1c06753d045ad10e00e7aba53ee2adba0712cccMatthew Williams                .setPersisted(true);
2003d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
2013d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        PersistableBundle extras = new PersistableBundle();
2023d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        extras.putDouble("hello", 3.2);
2033d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        extras.putString("hi", "there");
2043d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        extras.putInt("into", 3);
2053d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        b.setExtras(extras);
2067060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        final JobInfo task = b.build();
2071085ff6ee531931ef7f55cbadbc83616f619b292Dianne Hackborn        JobStatus taskStatus = JobStatus.createFromJobInfo(task, SOME_UID, null, -1, null);
2083d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
2093d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mTaskStoreUnderTest.add(taskStatus);
210d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        waitForPendingIo();
2113d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
2122f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        final JobSet jobStatusSet = new JobSet();
213616541d7016ee4440051738a8f0c7c3b7086d375Christopher Tate        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
21401ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
2152f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
21601ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams        assertTasksEqual(task, loaded.getJob());
2173d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
218d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
219d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    @Test
2208e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge    public void testWritingTaskWithSourcePackage() throws Exception {
2218e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge        JobInfo.Builder b = new Builder(8, mComponent)
2228e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                .setRequiresDeviceIdle(true)
2238e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                .setPeriodic(10000L)
2248e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                .setRequiresCharging(true)
2258e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                .setPersisted(true);
226b0001f6fb1383d9824c2733896b0b348e7f77240Dianne Hackborn        JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID,
2271085ff6ee531931ef7f55cbadbc83616f619b292Dianne Hackborn                "com.google.android.gms", 0, null);
2288e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge
2298e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge        mTaskStoreUnderTest.add(taskStatus);
230d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        waitForPendingIo();
2318e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge
2322f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        final JobSet jobStatusSet = new JobSet();
233616541d7016ee4440051738a8f0c7c3b7086d375Christopher Tate        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
2348e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge        assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
2352f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
2368e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge        assertEquals("Source package not equal.", loaded.getSourcePackageName(),
2378e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                taskStatus.getSourcePackageName());
2388e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge        assertEquals("Source user not equal.", loaded.getSourceUserId(),
2398e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                taskStatus.getSourceUserId());
2408e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge    }
2418e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge
242d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    @Test
2438e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge    public void testWritingTaskWithFlex() throws Exception {
2448e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge        JobInfo.Builder b = new Builder(8, mComponent)
2458e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                .setRequiresDeviceIdle(true)
2468e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                .setPeriodic(5*60*60*1000, 1*60*60*1000)
2478e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                .setRequiresCharging(true)
2488e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                .setPersisted(true);
2491085ff6ee531931ef7f55cbadbc83616f619b292Dianne Hackborn        JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
2508e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge
2518e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge        mTaskStoreUnderTest.add(taskStatus);
252d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        waitForPendingIo();
2538e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge
2542f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        final JobSet jobStatusSet = new JobSet();
255616541d7016ee4440051738a8f0c7c3b7086d375Christopher Tate        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
2568e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge        assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
2572f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
2588e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge        assertEquals("Period not equal.", loaded.getJob().getIntervalMillis(),
2598e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                taskStatus.getJob().getIntervalMillis());
2608e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge        assertEquals("Flex not equal.", loaded.getJob().getFlexMillis(),
2618e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                taskStatus.getJob().getFlexMillis());
2628e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge    }
2633d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
264d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    @Test
265fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams    public void testMassivePeriodClampedOnRead() throws Exception {
2668e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge        final long ONE_HOUR = 60*60*1000L; // flex
2678e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge        final long TWO_HOURS = 2 * ONE_HOUR; // period
268fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams        JobInfo.Builder b = new Builder(8, mComponent)
2698e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                .setPeriodic(TWO_HOURS, ONE_HOUR)
270fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams                .setPersisted(true);
271616541d7016ee4440051738a8f0c7c3b7086d375Christopher Tate        final long rtcNow = System.currentTimeMillis();
272fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams        final long invalidLateRuntimeElapsedMillis =
2738e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                SystemClock.elapsedRealtime() + (TWO_HOURS * ONE_HOUR) + TWO_HOURS;  // > period+flex
274fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams        final long invalidEarlyRuntimeElapsedMillis =
2758e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                invalidLateRuntimeElapsedMillis - TWO_HOURS;  // Early is (late - period).
276616541d7016ee4440051738a8f0c7c3b7086d375Christopher Tate        final Pair<Long, Long> persistedExecutionTimesUTC = new Pair<>(rtcNow, rtcNow + ONE_HOUR);
277b0001f6fb1383d9824c2733896b0b348e7f77240Dianne Hackborn        final JobStatus js = new JobStatus(b.build(), SOME_UID, "somePackage",
278a732f014c5743af0dbb7eb2e63474a7147576f9dChristopher Tate                0 /* sourceUserId */, 0, 0, "someTag",
279ab8a67fa78fc8e03ae8eb73d633d3e5e70867f02Makoto Onuki                invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis,
280616541d7016ee4440051738a8f0c7c3b7086d375Christopher Tate                0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
28115407846682b269e6b7fd0c24b84f709257fab5dMakoto Onuki                persistedExecutionTimesUTC, 0 /* innerFlagg */);
282fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams
283fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams        mTaskStoreUnderTest.add(js);
284d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        waitForPendingIo();
285fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams
2862f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        final JobSet jobStatusSet = new JobSet();
287616541d7016ee4440051738a8f0c7c3b7086d375Christopher Tate        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
288fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams        assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
2892f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
290fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams
291fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams        // Assert early runtime was clamped to be under now + period. We can do <= here b/c we'll
292fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams        // call SystemClock.elapsedRealtime after doing the disk i/o.
293fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams        final long newNowElapsed = SystemClock.elapsedRealtime();
294fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams        assertTrue("Early runtime wasn't correctly clamped.",
2958e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                loaded.getEarliestRunTime() <= newNowElapsed + TWO_HOURS);
2968e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge        // Assert late runtime was clamped to be now + period + flex.
297fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams        assertTrue("Early runtime wasn't correctly clamped.",
2988e64e2e6a4d2964b6a4147f5cccd03de934c86cdShreyas Basarge                loaded.getEarliestRunTime() <= newNowElapsed + TWO_HOURS + ONE_HOUR);
2995db09084c8e4efc6311754243c39962fc8e7a766Shreyas Basarge    }
3005db09084c8e4efc6311754243c39962fc8e7a766Shreyas Basarge
301d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    @Test
3025db09084c8e4efc6311754243c39962fc8e7a766Shreyas Basarge    public void testPriorityPersisted() throws Exception {
3035db09084c8e4efc6311754243c39962fc8e7a766Shreyas Basarge        JobInfo.Builder b = new Builder(92, mComponent)
3045db09084c8e4efc6311754243c39962fc8e7a766Shreyas Basarge                .setOverrideDeadline(5000)
3055db09084c8e4efc6311754243c39962fc8e7a766Shreyas Basarge                .setPriority(42)
3065db09084c8e4efc6311754243c39962fc8e7a766Shreyas Basarge                .setPersisted(true);
3071085ff6ee531931ef7f55cbadbc83616f619b292Dianne Hackborn        final JobStatus js = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
3085db09084c8e4efc6311754243c39962fc8e7a766Shreyas Basarge        mTaskStoreUnderTest.add(js);
309d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        waitForPendingIo();
310d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
3112f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        final JobSet jobStatusSet = new JobSet();
312616541d7016ee4440051738a8f0c7c3b7086d375Christopher Tate        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
3132f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
3145db09084c8e4efc6311754243c39962fc8e7a766Shreyas Basarge        assertEquals("Priority not correctly persisted.", 42, loaded.getPriority());
315fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams    }
316fa8e5084eed63ab8d92c71fcff656690a30293c3Matthew Williams
3173d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    /**
3187ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge     * Test that non persisted job is not written to disk.
3197ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge     */
320d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    @Test
3217ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge    public void testNonPersistedTaskIsNotPersisted() throws Exception {
3227ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge        JobInfo.Builder b = new Builder(42, mComponent)
3237ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge                .setOverrideDeadline(10000)
3247ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge                .setPersisted(false);
3251085ff6ee531931ef7f55cbadbc83616f619b292Dianne Hackborn        JobStatus jsNonPersisted = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
3267ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge        mTaskStoreUnderTest.add(jsNonPersisted);
3277ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge        b = new Builder(43, mComponent)
3287ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge                .setOverrideDeadline(10000)
3297ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge                .setPersisted(true);
3301085ff6ee531931ef7f55cbadbc83616f619b292Dianne Hackborn        JobStatus jsPersisted = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
3317ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge        mTaskStoreUnderTest.add(jsPersisted);
332d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        waitForPendingIo();
333d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
3342f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        final JobSet jobStatusSet = new JobSet();
335616541d7016ee4440051738a8f0c7c3b7086d375Christopher Tate        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
3367ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge        assertEquals("Job count is incorrect.", 1, jobStatusSet.size());
3372f36fd6fc94b62b8ccd03cdcea89826d05414f93Christopher Tate        JobStatus jobStatus = jobStatusSet.getAllJobs().iterator().next();
3387ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge        assertEquals("Wrong job persisted.", 43, jobStatus.getJobId());
3397ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge    }
3407ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge
341d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    @Test
342d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    public void testRequiredNetworkType() throws Exception {
343d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
344d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setPersisted(true)
345d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setRequiresDeviceIdle(true)
346d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE).build());
347d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
348d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setPersisted(true)
349d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build());
350d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
351d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setPersisted(true)
352d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED).build());
353d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
354d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setPersisted(true)
355d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING).build());
356d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
357d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setPersisted(true)
358d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_CELLULAR).build());
359d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    }
360d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
361d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    @Test
362d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    public void testRequiredNetwork() throws Exception {
363d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
364d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setPersisted(true)
365d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setRequiresDeviceIdle(true)
366d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setRequiredNetwork(null).build());
367d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
368d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setPersisted(true)
369d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setRequiredNetwork(new NetworkRequest.Builder().build()).build());
370d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
371d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setPersisted(true)
372d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setRequiredNetwork(new NetworkRequest.Builder()
373d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                        .addTransportType(TRANSPORT_WIFI).build())
374d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .build());
375d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
376d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setPersisted(true)
377d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .setRequiredNetwork(new NetworkRequest.Builder()
3783ec36ef2af10558a0a94aa56331ce16bc6b18914Pavel Maltsev                        .addCapability(NET_CAPABILITY_IMS)
3793ec36ef2af10558a0a94aa56331ce16bc6b18914Pavel Maltsev                        .addUnwantedCapability(NET_CAPABILITY_OEM_PAID)
3803ec36ef2af10558a0a94aa56331ce16bc6b18914Pavel Maltsev                        .build())
381d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                .build());
382d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    }
383d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
384d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    /**
385d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey     * Helper function to kick a {@link JobInfo} through a persistence cycle and
386d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey     * assert that it's unchanged.
387d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey     */
388d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    private void assertPersistedEquals(JobInfo first) throws Exception {
389d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        mTaskStoreUnderTest.clear();
390d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        mTaskStoreUnderTest.add(JobStatus.createFromJobInfo(first, SOME_UID, null, -1, null));
391d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        waitForPendingIo();
392d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
393d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        final JobSet jobStatusSet = new JobSet();
394d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
395d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        final JobStatus second = jobStatusSet.getAllJobs().iterator().next();
396d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        assertTasksEqual(first, second.getJob());
397d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    }
398d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
3997ef490fab68ee93f92eb5728cc4d3cb691315412Shreyas Basarge    /**
4003d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     * Helper function to throw an error if the provided task and TaskStatus objects are not equal.
4013d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     */
4027060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    private void assertTasksEqual(JobInfo first, JobInfo second) {
4033d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Different task ids.", first.getId(), second.getId());
4043d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Different components.", first.getService(), second.getService());
4053d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Different periodic status.", first.isPeriodic(), second.isPeriodic());
4063d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Different period.", first.getIntervalMillis(), second.getIntervalMillis());
4073d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Different inital backoff.", first.getInitialBackoffMillis(),
4083d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                second.getInitialBackoffMillis());
4093d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Different backoff policy.", first.getBackoffPolicy(),
4103d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                second.getBackoffPolicy());
4113d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
4123d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Invalid charging constraint.", first.isRequireCharging(),
4133d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                second.isRequireCharging());
414a06ec6a9435f9555142e700f54cf20278bc1982fDianne Hackborn        assertEquals("Invalid battery not low constraint.", first.isRequireBatteryNotLow(),
415a06ec6a9435f9555142e700f54cf20278bc1982fDianne Hackborn                second.isRequireBatteryNotLow());
4163d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Invalid idle constraint.", first.isRequireDeviceIdle(),
4173d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                second.isRequireDeviceIdle());
418d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        assertEquals("Invalid network type.",
419d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                first.getNetworkType(), second.getNetworkType());
420d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        assertEquals("Invalid network.",
421d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey                first.getRequiredNetwork(), second.getRequiredNetwork());
4223d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Invalid deadline constraint.",
4233d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                first.hasLateConstraint(),
4243d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                second.hasLateConstraint());
4253d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Invalid delay constraint.",
4263d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                first.hasEarlyConstraint(),
4273d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                second.hasEarlyConstraint());
4283d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Extras don't match",
4293d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                first.getExtras().toString(), second.getExtras().toString());
430ba60473a6539d16bef8720d79b5559512303bddfDianne Hackborn        assertEquals("Transient xtras don't match",
431ba60473a6539d16bef8720d79b5559512303bddfDianne Hackborn                first.getTransientExtras().toString(), second.getTransientExtras().toString());
432d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
433d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        // Since people can forget to add tests here for new fields, do one last
434d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        // sanity check based on bits-on-wire equality.
435d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        final byte[] firstBytes = marshall(first);
436d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        final byte[] secondBytes = marshall(second);
437d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        if (!Arrays.equals(firstBytes, secondBytes)) {
438d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey            Log.w(TAG, "First: " + HexDump.dumpHexString(firstBytes));
439d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey            Log.w(TAG, "Second: " + HexDump.dumpHexString(secondBytes));
440d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey            fail("Raw JobInfo aren't equal; see logs for details");
441d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        }
442d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    }
443d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey
444d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey    private static byte[] marshall(Parcelable p) {
445d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        final Parcel parcel = Parcel.obtain();
446d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        try {
447d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey            p.writeToParcel(parcel, 0);
448d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey            return parcel.marshall();
449d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        } finally {
450d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey            parcel.recycle();
451d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        }
4523d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
4533d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
4543d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    /**
4553d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     * When comparing timestamps before and after DB read/writes (to make sure we're saving/loading
4563d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     * the correct values), there is some latency involved that terrorises a naive assertEquals().
4573d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     * We define a <code>DELTA_MILLIS</code> as a function variable here to make this comparision
4583d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     * more reasonable.
4593d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     */
4603d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    private void compareTimestampsSubjectToIoLatency(String error, long ts1, long ts2) {
4613d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        final long DELTA_MILLIS = 700L;  // We allow up to 700ms of latency for IO read/writes.
462d0fff2eac4fe878071dd170e885a4a9c0a1b20e8Jeff Sharkey        assertTrue(error, Math.abs(ts1 - ts2) < DELTA_MILLIS);
4633d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
4643d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
4653d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    private static class StubClass {}
4663d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
46701ac45b6ff2334925c8d24b5278b44e5e30f5622Matthew Williams}
468