SystemUpdatePolicyTest.java revision 1b2f37401818b04cf4908d5aa9017eab44fe5662
1/*
2 * Copyright (C) 2018 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 */
16package com.android.server.devicepolicy;
17
18import static android.app.admin.SystemUpdatePolicy.ValidationFailedException.ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE;
19import static android.app.admin.SystemUpdatePolicy.ValidationFailedException.ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG;
20import static android.app.admin.SystemUpdatePolicy.ValidationFailedException.ERROR_DUPLICATE_OR_OVERLAP;
21import static android.app.admin.SystemUpdatePolicy.ValidationFailedException.ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE;
22import static android.app.admin.SystemUpdatePolicy.ValidationFailedException.ERROR_NEW_FREEZE_PERIOD_TOO_LONG;
23import static org.junit.Assert.assertEquals;
24import static org.junit.Assert.assertTrue;
25import static org.junit.Assert.fail;
26
27import android.app.admin.FreezePeriod;
28import android.app.admin.SystemUpdatePolicy;
29import android.os.Parcel;
30import android.support.test.runner.AndroidJUnit4;
31import android.util.Pair;
32import android.util.Xml;
33
34import com.android.internal.util.FastXmlSerializer;
35
36import org.junit.Test;
37import org.junit.runner.RunWith;
38import org.xmlpull.v1.XmlPullParser;
39import org.xmlpull.v1.XmlSerializer;
40
41import java.io.ByteArrayInputStream;
42import java.io.ByteArrayOutputStream;
43import java.io.InputStreamReader;
44import java.nio.charset.StandardCharsets;
45import java.time.LocalDate;
46import java.time.LocalDateTime;
47import java.time.MonthDay;
48import java.time.ZoneId;
49import java.util.ArrayList;
50import java.util.List;
51import java.util.concurrent.TimeUnit;
52
53
54/**
55 * Unit tests for {@link android.app.admin.SystemUpdatePolicy}.
56 * Throughout this test, we use "MM-DD" format to denote dates without year.
57 *
58 * atest com.android.server.devicepolicy.SystemUpdatePolicyTest
59 * runtest -c com.android.server.devicepolicy.SystemUpdatePolicyTest frameworks-services
60 */
61@RunWith(AndroidJUnit4.class)
62public final class SystemUpdatePolicyTest {
63
64    private static final int DUPLICATE_OR_OVERLAP = ERROR_DUPLICATE_OR_OVERLAP;
65    private static final int TOO_LONG = ERROR_NEW_FREEZE_PERIOD_TOO_LONG;
66    private static final int TOO_CLOSE = ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE;
67    private static final int COMBINED_TOO_LONG = ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG;
68    private static final int COMBINED_TOO_CLOSE = ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE;
69
70    @Test
71    public void testSimplePeriod() throws Exception {
72        testFreezePeriodsSucceeds("01-01", "01-02");
73        testFreezePeriodsSucceeds("01-31", "01-31");
74        testFreezePeriodsSucceeds("11-01", "01-15");
75        testFreezePeriodsSucceeds("02-01", "02-29"); // Leap year
76        testFreezePeriodsSucceeds("02-01", "03-01");
77        testFreezePeriodsSucceeds("12-01", "01-30"); // Wrapped Period
78        testFreezePeriodsSucceeds("11-02", "01-30", "04-01", "04-30"); // Wrapped Period
79    }
80
81    @Test
82    public void testCanonicalizationValidation() throws Exception {
83        testFreezePeriodsSucceeds("03-01", "03-31", "09-01", "09-30");
84        testFreezePeriodsSucceeds("06-01", "07-01", "09-01", "09-30");
85        testFreezePeriodsSucceeds("10-01", "10-31", "12-31", "01-31");
86        testFreezePeriodsSucceeds("01-01", "01-30", "04-01", "04-30");
87        testFreezePeriodsSucceeds("01-01", "02-28", "05-01", "06-30", "09-01", "10-31");
88
89        // One interval fully covers the other
90        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "03-01", "03-31", "03-15", "03-31");
91        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "03-01", "03-31", "03-15", "03-16");
92        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "11-15", "01-31", "12-01", "12-31");
93        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "12-01", "01-31", "01-01", "01-15");
94
95        // Partial overlap
96        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "03-01", "03-31", "03-15", "01-01");
97        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "11-15", "01-31", "12-01", "02-28");
98
99        // No gap between two intervals
100        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "01-31", "01-31", "02-01", "02-01");
101        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "12-01", "12-15", "12-15", "02-01");
102        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "12-01", "12-15", "12-16", "02-01");
103        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "12-01", "01-15", "01-15", "02-01");
104        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "12-01", "01-15", "01-16", "02-01");
105        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "01-01", "01-30", "12-01", "12-31");
106        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "12-01", "12-31", "04-01", "04-01",
107                "01-01", "01-30");
108    }
109
110    @Test
111    public void testLengthValidation() throws Exception {
112        testFreezePeriodsSucceeds("03-01", "03-31");
113        testFreezePeriodsSucceeds("03-03", "03-03", "12-31", "01-01");
114        testFreezePeriodsSucceeds("01-01", "03-31", "06-01", "08-29");
115        // entire year
116        testFreezePeriodsFails(TOO_LONG, "01-01", "12-31");
117        // long period spanning across year end
118        testFreezePeriodsSucceeds("11-01", "01-29");
119        testFreezePeriodsFails(TOO_LONG, "11-01", "01-30");
120        // Leap year handling
121        testFreezePeriodsSucceeds("12-01", "02-28");
122        testFreezePeriodsSucceeds("12-01", "02-29");
123        testFreezePeriodsFails(TOO_LONG, "12-01", "03-01");
124        // Regular long period
125        testFreezePeriodsSucceeds("01-01", "03-31", "06-01", "08-29");
126        testFreezePeriodsFails(TOO_LONG, "01-01", "03-31", "06-01", "08-30");
127    }
128
129    @Test
130    public void testSeparationValidation() throws Exception {
131        testFreezePeriodsSucceeds("01-01", "03-31", "06-01", "08-29");
132        testFreezePeriodsFails(TOO_CLOSE, "01-01", "01-01", "01-03", "01-03");
133        testFreezePeriodsFails(TOO_CLOSE, "03-01", "03-31", "05-01", "05-31");
134        // Short interval spans across end of year
135        testFreezePeriodsSucceeds("01-31", "03-01", "11-01", "12-01");
136        testFreezePeriodsFails(TOO_CLOSE, "01-30", "03-01", "11-01", "12-01");
137        // Short separation is after wrapped period
138        testFreezePeriodsSucceeds("03-03", "03-31", "12-31", "01-01");
139        testFreezePeriodsFails(TOO_CLOSE, "03-02", "03-31", "12-31", "01-01");
140        // Short separation including Feb 29
141        testFreezePeriodsSucceeds("12-01", "01-15", "03-17", "04-01");
142        testFreezePeriodsFails(TOO_CLOSE, "12-01", "01-15", "03-16", "04-01");
143        // Short separation including Feb 29
144        testFreezePeriodsSucceeds("01-01", "02-28", "04-30", "06-01");
145        testFreezePeriodsSucceeds("01-01", "02-29", "04-30", "06-01");
146        testFreezePeriodsFails(TOO_CLOSE, "01-01", "03-01", "04-30", "06-01");
147    }
148
149    @Test
150    public void testValidateTotalLengthWithPreviousPeriods() throws Exception {
151        testPrevFreezePeriodSucceeds("2018-01-19", "2018-01-19", /* now */"2018-01-19",
152                "07-01", "07-31", "10-01", "11-30");
153        testPrevFreezePeriodSucceeds("2018-01-01", "2018-01-19", /* now */"2018-01-19",
154                "01-01", "03-30");
155        testPrevFreezePeriodSucceeds("2018-01-01", "2018-02-01", /* now */"2018-02-01",
156                "11-01", "12-31");
157
158        testPrevFreezePeriodSucceeds("2017-11-01", "2018-01-02", /* now */"2018-01-02",
159                "01-01", "01-29");
160        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2017-11-01", "2018-01-02", "2018-01-02",
161                "01-01", "01-30");
162        testPrevFreezePeriodSucceeds("2017-11-01", "2018-01-02", /* now */"2018-01-01",
163                "01-02", "01-29");
164        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2017-11-01", "2018-01-02", "2018-01-01",
165                "01-02", "01-30");
166
167        testPrevFreezePeriodSucceeds("2017-11-01", "2017-12-01", /* now */"2017-12-01",
168                "11-15", "01-29");
169        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2017-11-01", "2017-12-01", "2017-12-01",
170                "11-15", "01-30");
171
172        testPrevFreezePeriodSucceeds("2017-11-01", "2018-01-01", /* now */"2018-01-01",
173                "11-15", "01-29");
174        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2017-11-01", "2018-01-01", "2018-01-01",
175                "11-15", "01-30");
176
177        testPrevFreezePeriodSucceeds("2018-03-01", "2018-03-31", /* now */"2018-03-31",
178                "04-01", "05-29");
179        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2018-03-01", "2018-03-31", "2018-03-31",
180                "04-01", "05-30");
181
182        // Leap year handing
183        testPrevFreezePeriodSucceeds("2017-12-01", "2018-01-02", /* now */"2018-01-02",
184                "01-01", "02-28");
185        testPrevFreezePeriodSucceeds("2017-12-01", "2018-01-02", /* now */"2018-01-02",
186                "01-01", "02-29");
187        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2017-12-01", "2018-01-02", "2018-01-02",
188                "01-01", "03-01");
189
190        testPrevFreezePeriodSucceeds("2016-01-01", "2016-02-28", /* now */"2016-02-28",
191                "02-01", "03-31");
192        testPrevFreezePeriodSucceeds("2016-01-01", "2016-02-28", /* now */"2016-02-29",
193                "02-01", "03-31");
194        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2016-01-01", "2016-02-28", "2016-02-29",
195                "02-01", "04-01");
196
197    }
198
199    @Test
200    public void testValidateSeparationWithPreviousPeriods() throws Exception {
201        testPrevFreezePeriodSucceeds("2018-01-01", "2018-01-02", /* now */"2018-03-04",
202                "01-01", "03-30");
203        testPrevFreezePeriodSucceeds("2018-01-01", "2018-01-02", /* now */"2018-01-19",
204                "04-01", "06-29");
205        testPrevFreezePeriodSucceeds("2017-01-01", "2017-03-30", /* now */"2018-12-01",
206                "01-01", "03-30");
207
208        testPrevFreezePeriodSucceeds("2018-01-01", "2018-02-01", "2018-02-01",
209                "04-03", "06-01");
210        testPrevFreezePeriodFails(COMBINED_TOO_CLOSE, "2018-01-01", "2018-02-01", "2018-02-01",
211                "04-02", "06-01");
212
213        testPrevFreezePeriodSucceeds("2018-04-01", "2018-06-01", "2018-08-01",
214                "07-01", "08-30");
215        testPrevFreezePeriodFails(COMBINED_TOO_CLOSE, "2018-04-01", "2018-06-01", "2018-07-30",
216                "07-01", "08-30");
217
218
219        testPrevFreezePeriodSucceeds("2018-03-01", "2018-04-01", "2018-06-01",
220                "05-01", "07-01");
221        testPrevFreezePeriodFails(COMBINED_TOO_CLOSE, "2018-03-01", "2018-04-01", "2018-05-31",
222                "05-01", "07-01");
223    }
224
225    @Test
226    public void testDistanceWithoutLeapYear() {
227        assertEquals(364, FreezePeriod.distanceWithoutLeapYear(
228                LocalDate.of(2016, 12, 31), LocalDate.of(2016, 1, 1)));
229        assertEquals(365, FreezePeriod.distanceWithoutLeapYear(
230                LocalDate.of(2017, 1, 1), LocalDate.of(2016, 1, 1)));
231        assertEquals(365, FreezePeriod.distanceWithoutLeapYear(
232                LocalDate.of(2017, 2, 28), LocalDate.of(2016, 2, 29)));
233        assertEquals(-365, FreezePeriod.distanceWithoutLeapYear(
234                LocalDate.of(2016, 1, 1), LocalDate.of(2017, 1, 1)));
235        assertEquals(1, FreezePeriod.distanceWithoutLeapYear(
236                LocalDate.of(2016, 3, 1), LocalDate.of(2016, 2, 29)));
237        assertEquals(1, FreezePeriod.distanceWithoutLeapYear(
238                LocalDate.of(2016, 3, 1), LocalDate.of(2016, 2, 28)));
239        assertEquals(0, FreezePeriod.distanceWithoutLeapYear(
240                LocalDate.of(2016, 2, 29), LocalDate.of(2016, 2, 28)));
241        assertEquals(0, FreezePeriod.distanceWithoutLeapYear(
242                LocalDate.of(2016, 2, 28), LocalDate.of(2016, 2, 28)));
243
244        assertEquals(59, FreezePeriod.distanceWithoutLeapYear(
245                LocalDate.of(2016, 3, 1), LocalDate.of(2016, 1, 1)));
246        assertEquals(59, FreezePeriod.distanceWithoutLeapYear(
247                LocalDate.of(2017, 3, 1), LocalDate.of(2017, 1, 1)));
248
249        assertEquals(365 * 40, FreezePeriod.distanceWithoutLeapYear(
250                LocalDate.of(2040, 1, 1), LocalDate.of(2000, 1, 1)));
251
252        assertEquals(365 * 2, FreezePeriod.distanceWithoutLeapYear(
253                LocalDate.of(2019, 3, 1), LocalDate.of(2017, 3, 1)));
254        assertEquals(365 * 2, FreezePeriod.distanceWithoutLeapYear(
255                LocalDate.of(2018, 3, 1), LocalDate.of(2016, 3, 1)));
256        assertEquals(365 * 2, FreezePeriod.distanceWithoutLeapYear(
257                LocalDate.of(2017, 3, 1), LocalDate.of(2015, 3, 1)));
258
259    }
260
261    @Test
262    public void testInstallationOptionWithoutFreeze() {
263        // Also duplicated at com.google.android.gts.deviceowner.SystemUpdatePolicyTest
264        final long millis_2018_01_01 = toMillis(2018, 1, 1);
265
266        SystemUpdatePolicy p = SystemUpdatePolicy.createAutomaticInstallPolicy();
267        assertInstallationOption(SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, Long.MAX_VALUE,
268                millis_2018_01_01, p);
269
270        p = SystemUpdatePolicy.createPostponeInstallPolicy();
271        assertInstallationOption(SystemUpdatePolicy.TYPE_POSTPONE, Long.MAX_VALUE,
272                millis_2018_01_01, p);
273
274        p = SystemUpdatePolicy.createWindowedInstallPolicy(120, 180); // 2:00 - 3:00
275        // 00:00 is two hours before the next window
276        assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(2),
277                millis_2018_01_01, p);
278        // 02:00 is within the current maintenance window, and one hour until the window ends
279        assertInstallationOption(
280                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.HOURS.toMillis(1),
281                millis_2018_01_01 + TimeUnit.HOURS.toMillis(2), p);
282        // 04:00 is 22 hours from the window next day
283        assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(22),
284                millis_2018_01_01 + TimeUnit.HOURS.toMillis(4), p);
285
286        p = SystemUpdatePolicy.createWindowedInstallPolicy(22 * 60, 2 * 60); // 22:00 - 2:00
287        // 21:00 is one hour from the next window
288        assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(1),
289                millis_2018_01_01 + TimeUnit.HOURS.toMillis(21), p);
290        // 00:00 is two hours from the end of current window
291        assertInstallationOption(
292                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.HOURS.toMillis(2),
293                millis_2018_01_01, p);
294        // 03:00 is 22 hours from the window today
295        assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(19),
296                millis_2018_01_01 + TimeUnit.HOURS.toMillis(3), p);
297    }
298
299    @Test
300    public void testInstallationOptionWithFreeze() throws Exception {
301        final long millis_2016_02_29 = toMillis(2016, 2, 29);
302        final long millis_2017_01_31 = toMillis(2017, 1, 31);
303        final long millis_2017_02_28 = toMillis(2017, 2, 28);
304        final long millis_2018_01_01 = toMillis(2018, 1, 1);
305        final long millis_2018_08_01 = toMillis(2018, 8, 1);
306
307        SystemUpdatePolicy p = SystemUpdatePolicy.createAutomaticInstallPolicy();
308        setFreezePeriods(p, "01-01", "01-31");
309        // Inside a freeze period
310        assertInstallationOption(
311                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(31),
312                millis_2018_01_01, p);
313        // Device is outside freeze between 2/28 to 12/31 inclusive
314        assertInstallationOption(
315                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(307),
316                millis_2017_02_28, p);
317
318        // Freeze period contains leap day Feb 29
319        p = SystemUpdatePolicy.createPostponeInstallPolicy();
320        setFreezePeriods(p, "02-01", "03-05");
321        // Freezed until 3/5, note 2016 is a leap year
322        assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(6),
323                millis_2016_02_29, p);
324        // Freezed until 3/5, note 2017 is not a leap year
325        assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(6),
326                millis_2017_02_28, p);
327        // Next freeze is 2018/2/1
328        assertInstallationOption(SystemUpdatePolicy.TYPE_POSTPONE, TimeUnit.DAYS.toMillis(31),
329                millis_2018_01_01, p);
330
331        // Freeze period start on or right after leap day
332        p = SystemUpdatePolicy.createAutomaticInstallPolicy();
333        setFreezePeriods(p, "03-01", "03-31");
334        assertInstallationOption(
335                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(1),
336                millis_2016_02_29, p);
337        assertInstallationOption(
338                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(1),
339                millis_2017_02_28, p);
340        setFreezePeriods(p, "02-28", "03-05");
341        assertInstallationOption(
342                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(6),
343                millis_2016_02_29, p);
344        assertInstallationOption(
345                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(6),
346                millis_2017_02_28, p);
347
348        // Freeze period end on or right after leap day
349        p = SystemUpdatePolicy.createAutomaticInstallPolicy();
350        setFreezePeriods(p, "02-01", "02-28");
351        assertInstallationOption(
352                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(1),
353                millis_2016_02_29, p);
354        assertInstallationOption(
355                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(1),
356                millis_2017_02_28, p);
357        p = SystemUpdatePolicy.createAutomaticInstallPolicy();
358        setFreezePeriods(p, "02-01", "03-01");
359        assertInstallationOption(
360                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(2),
361                millis_2016_02_29, p);
362        assertInstallationOption(
363                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(2),
364                millis_2017_02_28, p);
365
366        // Freeze period with maintenance window
367        p = SystemUpdatePolicy.createWindowedInstallPolicy(23 * 60, 1 * 60); // 23:00 - 1:00
368        setFreezePeriods(p, "02-01", "02-28");
369        // 00:00 is within the current window, outside freeze period
370        assertInstallationOption(
371                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.HOURS.toMillis(1),
372                millis_2018_01_01, p);
373        // Last day of feeze period, which ends in 22 hours
374        assertInstallationOption(
375                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(22),
376                millis_2017_02_28 + TimeUnit.HOURS.toMillis(2), p);
377        // Last day before the next freeze, and within window
378        assertInstallationOption(
379                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.HOURS.toMillis(1),
380                millis_2017_01_31, p);
381        // Last day before the next freeze, and there is still a partial maintenance window before
382        // the freeze.
383        assertInstallationOption(
384                SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(19),
385                millis_2017_01_31 + TimeUnit.HOURS.toMillis(4), p);
386
387        // Two freeze periods
388        p = SystemUpdatePolicy.createAutomaticInstallPolicy();
389        setFreezePeriods(p, "05-01", "06-01", "10-15", "01-10");
390        // automatic policy for July, August, September and October until 15th
391        assertInstallationOption(
392                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(31 + 30 + 14),
393                millis_2018_08_01, p);
394    }
395
396    private void assertInstallationOption(int expectedType, long expectedTime, long now,
397            SystemUpdatePolicy p) {
398        assertEquals(expectedType, p.getInstallationOptionAt(now).getType());
399        assertEquals(expectedTime, p.getInstallationOptionAt(now).getEffectiveTime());
400    }
401
402    private void testFreezePeriodsSucceeds(String...dates) throws Exception {
403        SystemUpdatePolicy p = SystemUpdatePolicy.createPostponeInstallPolicy();
404        setFreezePeriods(p, dates);
405    }
406
407    private void testFreezePeriodsFails(int expectedError, String... dates) throws Exception {
408        SystemUpdatePolicy p = SystemUpdatePolicy.createPostponeInstallPolicy();
409        try {
410            setFreezePeriods(p, dates);
411            fail("Invalid periods (" + expectedError + ") not flagged: " + String.join(" ", dates));
412        } catch (SystemUpdatePolicy.ValidationFailedException e) {
413            assertTrue("Exception not expected: " + e.getMessage(),
414                    e.getErrorCode() == expectedError);
415        }
416    }
417
418    private void testPrevFreezePeriodSucceeds(String prevStart, String prevEnd, String now,
419            String... dates) throws Exception {
420        createPrevFreezePeriod(prevStart, prevEnd, now, dates);
421    }
422
423    private void testPrevFreezePeriodFails(int expectedError, String prevStart, String prevEnd,
424            String now,  String... dates) throws Exception {
425        try {
426            createPrevFreezePeriod(prevStart, prevEnd, now, dates);
427            fail("Invalid period (" + expectedError + ") not flagged: " + String.join(" ", dates));
428        } catch (SystemUpdatePolicy.ValidationFailedException e) {
429            assertTrue("Exception not expected: " + e.getMessage(),
430                    e.getErrorCode() == expectedError);
431        }
432    }
433
434    private void createPrevFreezePeriod(String prevStart, String prevEnd, String now,
435            String... dates) throws Exception {
436        SystemUpdatePolicy p = SystemUpdatePolicy.createPostponeInstallPolicy();
437        setFreezePeriods(p, dates);
438        p.validateAgainstPreviousFreezePeriod(parseLocalDate(prevStart),
439                parseLocalDate(prevEnd), parseLocalDate(now));
440    }
441
442    // "MM-DD" format for date
443    private void setFreezePeriods(SystemUpdatePolicy policy, String... dates) throws Exception {
444        List<FreezePeriod> periods = new ArrayList<>();
445        MonthDay lastDate = null;
446        for (String date : dates) {
447            MonthDay currentDate = parseMonthDay(date);
448            if (lastDate != null) {
449                periods.add(new FreezePeriod(lastDate, currentDate));
450                lastDate = null;
451            } else {
452                lastDate = currentDate;
453            }
454        }
455        policy.setFreezePeriods(periods);
456        testSerialization(policy, periods);
457    }
458
459    private void testSerialization(SystemUpdatePolicy policy,
460            List<FreezePeriod> expectedPeriods) throws Exception {
461        // Test parcel / unparcel
462        Parcel parcel = Parcel.obtain();
463        policy.writeToParcel(parcel, 0);
464        parcel.setDataPosition(0);
465        SystemUpdatePolicy q = SystemUpdatePolicy.CREATOR.createFromParcel(parcel);
466        checkFreezePeriods(q, expectedPeriods);
467        parcel.recycle();
468
469        // Test XML serialization
470        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
471        final XmlSerializer outXml = new FastXmlSerializer();
472        outXml.setOutput(outStream, StandardCharsets.UTF_8.name());
473        outXml.startDocument(null, true);
474        outXml.startTag(null, "ota");
475        policy.saveToXml(outXml);
476        outXml.endTag(null, "ota");
477        outXml.endDocument();
478        outXml.flush();
479
480        ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
481        XmlPullParser parser = Xml.newPullParser();
482        parser.setInput(new InputStreamReader(inStream));
483        assertEquals(XmlPullParser.START_TAG, parser.next());
484        checkFreezePeriods(SystemUpdatePolicy.restoreFromXml(parser), expectedPeriods);
485    }
486
487    private void checkFreezePeriods(SystemUpdatePolicy policy,
488            List<FreezePeriod> expectedPeriods) {
489        int i = 0;
490        for (FreezePeriod period : policy.getFreezePeriods()) {
491            assertEquals(expectedPeriods.get(i).getStart(), period.getStart());
492            assertEquals(expectedPeriods.get(i).getEnd(), period.getEnd());
493            i++;
494        }
495    }
496
497    // MonthDay is of format MM-dd
498    private MonthDay parseMonthDay(String date) {
499        return MonthDay.of(Integer.parseInt(date.substring(0, 2)),
500                Integer.parseInt(date.substring(3, 5)));
501    }
502
503    // LocalDat is of format YYYY-MM-dd
504    private LocalDate parseLocalDate(String date) {
505        return parseMonthDay(date.substring(5)).atYear(Integer.parseInt(date.substring(0, 4)));
506    }
507
508
509    private long toMillis(int year, int month, int day) {
510        return LocalDateTime.of(year, month, day, 0, 0, 0).atZone(ZoneId.systemDefault())
511                .toInstant().toEpochMilli();
512    }
513}
514