SystemUpdatePolicyTest.java revision 29b9a7d1f4168d888ee2f4a0ff3882523f655e6b
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.FreezeInterval;
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.util.ArrayList;
47import java.util.List;
48
49/**
50 * Unit tests for {@link android.app.admin.SystemUpdatePolicy}.
51 * Throughout this test, we use "MM-DD" format to denote dates without year.
52 *
53 * atest com.android.server.devicepolicy.SystemUpdatePolicyTest
54 * runtest -c com.android.server.devicepolicy.SystemUpdatePolicyTest frameworks-services
55 */
56@RunWith(AndroidJUnit4.class)
57public final class SystemUpdatePolicyTest {
58
59    private static final int DUPLICATE_OR_OVERLAP = ERROR_DUPLICATE_OR_OVERLAP;
60    private static final int TOO_LONG = ERROR_NEW_FREEZE_PERIOD_TOO_LONG;
61    private static final int TOO_CLOSE = ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE;
62    private static final int COMBINED_TOO_LONG = ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG;
63    private static final int COMBINED_TOO_CLOSE = ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE;
64
65    @Test
66    public void testSimplePeriod() throws Exception {
67        testFreezePeriodsSucceeds("01-01", "01-02");
68        testFreezePeriodsSucceeds("01-31", "01-31");
69        testFreezePeriodsSucceeds("11-01", "01-15");
70        testFreezePeriodsSucceeds("02-01", "02-29"); // Leap year
71        testFreezePeriodsSucceeds("02-01", "03-01");
72        testFreezePeriodsSucceeds("12-01", "01-30"); // Wrapped Period
73        testFreezePeriodsSucceeds("11-02", "01-30", "04-01", "04-30"); // Wrapped Period
74    }
75
76    @Test
77    public void testCanonicalizationValidation() throws Exception {
78        testFreezePeriodsSucceeds("03-01", "03-31", "09-01", "09-30");
79        testFreezePeriodsSucceeds("06-01", "07-01", "09-01", "09-30");
80        testFreezePeriodsSucceeds("10-01", "10-31", "12-31", "01-31");
81        testFreezePeriodsSucceeds("01-01", "01-30", "04-01", "04-30");
82        testFreezePeriodsSucceeds("01-01", "02-28", "05-01", "06-30", "09-01", "10-31");
83
84        // One interval fully covers the other
85        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "03-01", "03-31", "03-15", "03-31");
86        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "03-01", "03-31", "03-15", "03-16");
87        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "11-15", "01-31", "12-01", "12-31");
88        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "12-01", "01-31", "01-01", "01-15");
89
90        // Partial overlap
91        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "03-01", "03-31", "03-15", "01-01");
92        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "11-15", "01-31", "12-01", "02-28");
93
94        // No gap between two intervals
95        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "01-31", "01-31", "02-01", "02-01");
96        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "12-01", "12-15", "12-15", "02-01");
97        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "12-01", "12-15", "12-16", "02-01");
98        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "12-01", "01-15", "01-15", "02-01");
99        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "12-01", "01-15", "01-16", "02-01");
100        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "01-01", "01-30", "12-01", "12-31");
101        testFreezePeriodsFails(DUPLICATE_OR_OVERLAP, "12-01", "12-31", "04-01", "04-01",
102                "01-01", "01-30");
103    }
104
105    @Test
106    public void testLengthValidation() throws Exception {
107        testFreezePeriodsSucceeds("03-01", "03-31");
108        testFreezePeriodsSucceeds("03-03", "03-03", "12-31", "01-01");
109        testFreezePeriodsSucceeds("01-01", "03-31", "06-01", "08-29");
110        // entire year
111        testFreezePeriodsFails(TOO_LONG, "01-01", "12-31");
112        // long period spanning across year end
113        testFreezePeriodsSucceeds("11-01", "01-29");
114        testFreezePeriodsFails(TOO_LONG, "11-01", "01-30");
115        // Leap year handling
116        testFreezePeriodsSucceeds("12-01", "02-28");
117        testFreezePeriodsSucceeds("12-01", "02-29");
118        testFreezePeriodsFails(TOO_LONG, "12-01", "03-01");
119        // Regular long period
120        testFreezePeriodsSucceeds("01-01", "03-31", "06-01", "08-29");
121        testFreezePeriodsFails(TOO_LONG, "01-01", "03-31", "06-01", "08-30");
122    }
123
124    @Test
125    public void testSeparationValidation() throws Exception {
126        testFreezePeriodsSucceeds("01-01", "03-31", "06-01", "08-29");
127        testFreezePeriodsFails(TOO_CLOSE, "01-01", "01-01", "01-03", "01-03");
128        testFreezePeriodsFails(TOO_CLOSE, "03-01", "03-31", "05-01", "05-31");
129        // Short interval spans across end of year
130        testFreezePeriodsSucceeds("01-31", "03-01", "11-01", "12-01");
131        testFreezePeriodsFails(TOO_CLOSE, "01-30", "03-01", "11-01", "12-01");
132        // Short separation is after wrapped period
133        testFreezePeriodsSucceeds("03-03", "03-31", "12-31", "01-01");
134        testFreezePeriodsFails(TOO_CLOSE, "03-02", "03-31", "12-31", "01-01");
135        // Short separation including Feb 29
136        testFreezePeriodsSucceeds("12-01", "01-15", "03-17", "04-01");
137        testFreezePeriodsFails(TOO_CLOSE, "12-01", "01-15", "03-16", "04-01");
138        // Short separation including Feb 29
139        testFreezePeriodsSucceeds("01-01", "02-28", "04-30", "06-01");
140        testFreezePeriodsSucceeds("01-01", "02-29", "04-30", "06-01");
141        testFreezePeriodsFails(TOO_CLOSE, "01-01", "03-01", "04-30", "06-01");
142    }
143
144    @Test
145    public void testValidateTotalLengthWithPreviousPeriods() throws Exception {
146        testPrevFreezePeriodSucceeds("2018-01-19", "2018-01-19", /* now */"2018-01-19",
147                "07-01", "07-31", "10-01", "11-30");
148        testPrevFreezePeriodSucceeds("2018-01-01", "2018-01-19", /* now */"2018-01-19",
149                "01-01", "03-30");
150        testPrevFreezePeriodSucceeds("2018-01-01", "2018-02-01", /* now */"2018-02-01",
151                "11-01", "12-31");
152
153        testPrevFreezePeriodSucceeds("2017-11-01", "2018-01-02", /* now */"2018-01-02",
154                "01-01", "01-29");
155        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2017-11-01", "2018-01-02", "2018-01-02",
156                "01-01", "01-30");
157        testPrevFreezePeriodSucceeds("2017-11-01", "2018-01-02", /* now */"2018-01-01",
158                "01-02", "01-29");
159        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2017-11-01", "2018-01-02", "2018-01-01",
160                "01-02", "01-30");
161
162        testPrevFreezePeriodSucceeds("2017-11-01", "2017-12-01", /* now */"2017-12-01",
163                "11-15", "01-29");
164        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2017-11-01", "2017-12-01", "2017-12-01",
165                "11-15", "01-30");
166
167        testPrevFreezePeriodSucceeds("2017-11-01", "2018-01-01", /* now */"2018-01-01",
168                "11-15", "01-29");
169        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2017-11-01", "2018-01-01", "2018-01-01",
170                "11-15", "01-30");
171
172        testPrevFreezePeriodSucceeds("2018-03-01", "2018-03-31", /* now */"2018-03-31",
173                "04-01", "05-29");
174        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2018-03-01", "2018-03-31", "2018-03-31",
175                "04-01", "05-30");
176
177        // Leap year handing
178        testPrevFreezePeriodSucceeds("2017-12-01", "2018-01-02", /* now */"2018-01-02",
179                "01-01", "02-28");
180        testPrevFreezePeriodSucceeds("2017-12-01", "2018-01-02", /* now */"2018-01-02",
181                "01-01", "02-29");
182        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2017-12-01", "2018-01-02", "2018-01-02",
183                "01-01", "03-01");
184
185        testPrevFreezePeriodSucceeds("2016-01-01", "2016-02-28", /* now */"2016-02-28",
186                "02-01", "03-31");
187        testPrevFreezePeriodSucceeds("2016-01-01", "2016-02-28", /* now */"2016-02-29",
188                "02-01", "03-31");
189        testPrevFreezePeriodFails(COMBINED_TOO_LONG, "2016-01-01", "2016-02-28", "2016-02-29",
190                "02-01", "04-01");
191
192    }
193
194    @Test
195    public void testValidateSeparationWithPreviousPeriods() throws Exception {
196        testPrevFreezePeriodSucceeds("2018-01-01", "2018-01-02", /* now */"2018-03-04",
197                "01-01", "03-30");
198        testPrevFreezePeriodSucceeds("2018-01-01", "2018-01-02", /* now */"2018-01-19",
199                "04-01", "06-29");
200        testPrevFreezePeriodSucceeds("2017-01-01", "2017-03-30", /* now */"2018-12-01",
201                "01-01", "03-30");
202
203        testPrevFreezePeriodSucceeds("2018-01-01", "2018-02-01", "2018-02-01",
204                "04-03", "06-01");
205        testPrevFreezePeriodFails(COMBINED_TOO_CLOSE, "2018-01-01", "2018-02-01", "2018-02-01",
206                "04-02", "06-01");
207
208        testPrevFreezePeriodSucceeds("2018-04-01", "2018-06-01", "2018-08-01",
209                "07-01", "08-30");
210        testPrevFreezePeriodFails(COMBINED_TOO_CLOSE, "2018-04-01", "2018-06-01", "2018-07-30",
211                "07-01", "08-30");
212
213
214        testPrevFreezePeriodSucceeds("2018-03-01", "2018-04-01", "2018-06-01",
215                "05-01", "07-01");
216        testPrevFreezePeriodFails(COMBINED_TOO_CLOSE, "2018-03-01", "2018-04-01", "2018-05-31",
217                "05-01", "07-01");
218    }
219
220    @Test
221    public void testDistanceWithoutLeapYear() {
222        assertEquals(364, FreezeInterval.distanceWithoutLeapYear(
223                LocalDate.of(2016, 12, 31), LocalDate.of(2016, 1, 1)));
224        assertEquals(365, FreezeInterval.distanceWithoutLeapYear(
225                LocalDate.of(2017, 1, 1), LocalDate.of(2016, 1, 1)));
226        assertEquals(365, FreezeInterval.distanceWithoutLeapYear(
227                LocalDate.of(2017, 2, 28), LocalDate.of(2016, 2, 29)));
228        assertEquals(-365, FreezeInterval.distanceWithoutLeapYear(
229                LocalDate.of(2016, 1, 1), LocalDate.of(2017, 1, 1)));
230        assertEquals(1, FreezeInterval.distanceWithoutLeapYear(
231                LocalDate.of(2016, 3, 1), LocalDate.of(2016, 2, 29)));
232        assertEquals(1, FreezeInterval.distanceWithoutLeapYear(
233                LocalDate.of(2016, 3, 1), LocalDate.of(2016, 2, 28)));
234        assertEquals(0, FreezeInterval.distanceWithoutLeapYear(
235                LocalDate.of(2016, 2, 29), LocalDate.of(2016, 2, 28)));
236        assertEquals(0, FreezeInterval.distanceWithoutLeapYear(
237                LocalDate.of(2016, 2, 28), LocalDate.of(2016, 2, 28)));
238
239        assertEquals(59, FreezeInterval.distanceWithoutLeapYear(
240                LocalDate.of(2016, 3, 1), LocalDate.of(2016, 1, 1)));
241        assertEquals(59, FreezeInterval.distanceWithoutLeapYear(
242                LocalDate.of(2017, 3, 1), LocalDate.of(2017, 1, 1)));
243
244        assertEquals(365 * 40, FreezeInterval.distanceWithoutLeapYear(
245                LocalDate.of(2040, 1, 1), LocalDate.of(2000, 1, 1)));
246
247        assertEquals(365 * 2, FreezeInterval.distanceWithoutLeapYear(
248                LocalDate.of(2019, 3, 1), LocalDate.of(2017, 3, 1)));
249        assertEquals(365 * 2, FreezeInterval.distanceWithoutLeapYear(
250                LocalDate.of(2018, 3, 1), LocalDate.of(2016, 3, 1)));
251        assertEquals(365 * 2, FreezeInterval.distanceWithoutLeapYear(
252                LocalDate.of(2017, 3, 1), LocalDate.of(2015, 3, 1)));
253
254    }
255
256    private void testFreezePeriodsSucceeds(String...dates) throws Exception {
257        SystemUpdatePolicy p = SystemUpdatePolicy.createPostponeInstallPolicy();
258        setFreezePeriods(p, dates);
259    }
260
261    private void testFreezePeriodsFails(int expectedError, String... dates) throws Exception {
262        SystemUpdatePolicy p = SystemUpdatePolicy.createPostponeInstallPolicy();
263        try {
264            setFreezePeriods(p, dates);
265            fail("Invalid periods (" + expectedError + ") not flagged: " + String.join(" ", dates));
266        } catch (SystemUpdatePolicy.ValidationFailedException e) {
267            assertTrue("Exception not expected: " + e.getMessage(),
268                    e.getErrorCode() == expectedError);
269        }
270    }
271
272    private void testPrevFreezePeriodSucceeds(String prevStart, String prevEnd, String now,
273            String... dates) throws Exception {
274        createPrevFreezePeriod(prevStart, prevEnd, now, dates);
275    }
276
277    private void testPrevFreezePeriodFails(int expectedError, String prevStart, String prevEnd,
278            String now,  String... dates) throws Exception {
279        try {
280            createPrevFreezePeriod(prevStart, prevEnd, now, dates);
281            fail("Invalid period (" + expectedError + ") not flagged: " + String.join(" ", dates));
282        } catch (SystemUpdatePolicy.ValidationFailedException e) {
283            assertTrue("Exception not expected: " + e.getMessage(),
284                    e.getErrorCode() == expectedError);
285        }
286    }
287
288    private void createPrevFreezePeriod(String prevStart, String prevEnd, String now,
289            String... dates) throws Exception {
290        SystemUpdatePolicy p = SystemUpdatePolicy.createPostponeInstallPolicy();
291        setFreezePeriods(p, dates);
292        p.validateAgainstPreviousFreezePeriod(parseDate(prevStart), parseDate(prevEnd),
293                parseDate(now));
294    }
295
296    // "MM-DD" format for date
297    private void setFreezePeriods(SystemUpdatePolicy policy, String... dates) throws Exception {
298        List<Pair<Integer, Integer>> periods = new ArrayList<>();
299        LocalDate lastDate = null;
300        for (String date : dates) {
301            LocalDate currentDate = parseDate(date);
302            if (lastDate != null) {
303                periods.add(new Pair<>(lastDate.getDayOfYear(), currentDate.getDayOfYear()));
304                lastDate = null;
305            } else {
306                lastDate = currentDate;
307            }
308        }
309        policy.setFreezePeriods(periods);
310        testSerialization(policy, periods);
311    }
312
313    private void testSerialization(SystemUpdatePolicy policy,
314            List<Pair<Integer, Integer>> expectedPeriods) throws Exception {
315        // Test parcel / unparcel
316        Parcel parcel = Parcel.obtain();
317        policy.writeToParcel(parcel, 0);
318        parcel.setDataPosition(0);
319        SystemUpdatePolicy q = SystemUpdatePolicy.CREATOR.createFromParcel(parcel);
320        checkFreezePeriods(q, expectedPeriods);
321        parcel.recycle();
322
323        // Test XML serialization
324        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
325        final XmlSerializer outXml = new FastXmlSerializer();
326        outXml.setOutput(outStream, StandardCharsets.UTF_8.name());
327        outXml.startDocument(null, true);
328        outXml.startTag(null, "ota");
329        policy.saveToXml(outXml);
330        outXml.endTag(null, "ota");
331        outXml.endDocument();
332        outXml.flush();
333
334        ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
335        XmlPullParser parser = Xml.newPullParser();
336        parser.setInput(new InputStreamReader(inStream));
337        assertEquals(XmlPullParser.START_TAG, parser.next());
338        checkFreezePeriods(SystemUpdatePolicy.restoreFromXml(parser), expectedPeriods);
339    }
340
341    private void checkFreezePeriods(SystemUpdatePolicy policy,
342            List<Pair<Integer, Integer>> expectedPeriods) {
343        int i = 0;
344        for (Pair<Integer, Integer> period : policy.getFreezePeriods()) {
345            assertEquals(expectedPeriods.get(i).first, period.first);
346            assertEquals(expectedPeriods.get(i).second, period.second);
347            i++;
348        }
349    }
350
351    private LocalDate parseDate(String date) {
352        // Use leap year when parsing date string to handle "02-29", but force round down
353        // to Feb 28th by overriding the year to non-leap year.
354        final int year;
355        boolean monthDateOnly = false;
356        if (date.length() == 5) {
357            year = 2000;
358            monthDateOnly = true;
359        } else {
360            year = Integer.parseInt(date.substring(0, 4));
361            date = date.substring(5);
362        }
363        LocalDate result = LocalDate.of(year, Integer.parseInt(date.substring(0, 2)),
364                Integer.parseInt(date.substring(3, 5)));
365        if (monthDateOnly) {
366            return result.withYear(2001);
367        } else {
368            return result;
369        }
370    }
371}
372