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