1 /*
2  * Copyright (C) 2015 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.cts.deviceowner;
17 
18 
19 import android.app.admin.DevicePolicyManager;
20 import android.app.admin.FreezePeriod;
21 import android.app.admin.SystemUpdatePolicy;
22 import android.app.admin.SystemUpdatePolicy.ValidationFailedException;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.icu.util.Calendar;
28 import android.provider.Settings;
29 import android.provider.Settings.Global;
30 import android.util.Pair;
31 
32 import java.time.LocalDate;
33 import java.time.MonthDay;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.concurrent.Semaphore;
37 import java.util.concurrent.TimeUnit;
38 
39 /**
40  * Test {@link SystemUpdatePolicy}, {@link DevicePolicyManager#setSystemUpdatePolicy} and
41  * {@link DevicePolicyManager#getSystemUpdatePolicy}
42  */
43 public class SystemUpdatePolicyTest extends BaseDeviceOwnerTest {
44 
45     private static final int TIMEOUT_MS = 20_000;
46 
47     private final Semaphore mPolicyChangedSemaphore = new Semaphore(0);
48     private final Semaphore mTimeChangedSemaphore = new Semaphore(0);
49     private final BroadcastReceiver policyChangedReceiver = new BroadcastReceiver() {
50         @Override
51         public void onReceive(Context context, Intent intent) {
52             final String action = intent.getAction();
53             if (DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED.equals(action)) {
54                 mPolicyChangedSemaphore.release();
55             } else if (Intent.ACTION_TIME_CHANGED.equals(action)) {
56                 mTimeChangedSemaphore.release();
57             }
58         }
59     };
60 
61     private int mSavedAutoTimeConfig;
62     private LocalDate mSavedSystemDate;
63     private boolean mRestoreDate;
64 
65     @Override
setUp()66     protected void setUp() throws Exception {
67         super.setUp();
68         IntentFilter filter = new IntentFilter();
69         filter.addAction(DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED);
70         filter.addAction(Intent.ACTION_TIME_CHANGED);
71         mContext.registerReceiver(policyChangedReceiver, filter);
72         clearFreezeRecord();
73         mSavedAutoTimeConfig = Settings.Global.getInt(mContext.getContentResolver(),
74                 Global.AUTO_TIME, 0);
75         executeShellCommand("settings put global auto_time 0");
76         mSavedSystemDate = LocalDate.now();
77         mRestoreDate = false;
78     }
79 
80     @Override
tearDown()81     protected void tearDown() throws Exception {
82         mDevicePolicyManager.setSystemUpdatePolicy(getWho(), null);
83         clearFreezeRecord();
84         if (mRestoreDate) {
85             setSystemDate(mSavedSystemDate);
86         }
87         executeShellCommand("settings put global auto_time",
88                 Integer.toString(mSavedAutoTimeConfig));
89         // This needs to happen last since setSystemDate() relies on the receiver for
90         // synchronization.
91         mContext.unregisterReceiver(policyChangedReceiver);
92         super.tearDown();
93     }
94 
testSetEmptytInstallPolicy()95     public void testSetEmptytInstallPolicy() {
96         testPolicy(null);
97     }
98 
testSetAutomaticInstallPolicy()99     public void testSetAutomaticInstallPolicy() {
100         testPolicy(SystemUpdatePolicy.createAutomaticInstallPolicy());
101     }
102 
testSetWindowedInstallPolicy()103     public void testSetWindowedInstallPolicy() {
104         testPolicy(SystemUpdatePolicy.createWindowedInstallPolicy(0, 720));
105     }
106 
testSetPostponeInstallPolicy()107     public void testSetPostponeInstallPolicy() {
108         testPolicy(SystemUpdatePolicy.createPostponeInstallPolicy());
109     }
110 
testShouldFailInvalidWindowPolicy()111     public void testShouldFailInvalidWindowPolicy() throws Exception {
112         try {
113             SystemUpdatePolicy.createWindowedInstallPolicy(24 * 60 + 1, 720);
114             fail("Invalid window start should not be accepted.");
115         } catch (IllegalArgumentException expected) { }
116         try {
117             SystemUpdatePolicy.createWindowedInstallPolicy(-1, 720);
118             fail("Invalid window start should not be accepted.");
119         } catch (IllegalArgumentException expected) { }
120         try {
121             SystemUpdatePolicy.createWindowedInstallPolicy(0, 24 * 60 + 1);
122             fail("Invalid window end should not be accepted.");
123         } catch (IllegalArgumentException expected) { }
124         try {
125             SystemUpdatePolicy.createWindowedInstallPolicy(0, -1);
126             fail("Invalid window end should not be accepted.");
127         } catch (IllegalArgumentException expected) { }
128     }
129 
testFreezePeriodValidation()130     public void testFreezePeriodValidation() {
131         // Dates are in MM-DD format
132         validateFreezePeriodsSucceeds("01-01", "01-02");
133         validateFreezePeriodsSucceeds("01-31", "01-31");
134         validateFreezePeriodsSucceeds("11-01", "01-15");
135         validateFreezePeriodsSucceeds("02-01", "02-29");
136         validateFreezePeriodsSucceeds("03-01", "03-31", "09-01", "09-30");
137         validateFreezePeriodsSucceeds("10-01", "10-31", "12-31", "01-31");
138         validateFreezePeriodsSucceeds("01-01", "02-28", "05-01", "06-30", "09-01", "10-31");
139         validateFreezePeriodsSucceeds("11-02", "01-15", "03-18", "04-30", "08-01", "08-30");
140 
141         // full overlap
142         validateFreezePeriodsFailsOverlap("12-01", "01-31", "12-25", "01-15");
143         // partial overlap
144         validateFreezePeriodsFailsOverlap("03-01", "03-31", "03-15", "01-01");
145         // touching interval
146         validateFreezePeriodsFailsOverlap("01-31", "01-31", "02-01", "02-01");
147         validateFreezePeriodsFailsOverlap("12-01", "12-31", "04-01", "04-01", "01-01", "01-30");
148 
149         // entire year
150         validateFreezePeriodsFailsTooLong("01-01", "12-31");
151         // Regular long period
152         validateFreezePeriodsSucceeds("01-01", "03-31", "06-01", "08-29");
153         validateFreezePeriodsFailsTooLong("01-01", "03-31", "06-01", "08-30");
154         // long period spanning across year end
155         validateFreezePeriodsSucceeds("11-01", "01-29");
156         validateFreezePeriodsFailsTooLong("11-01", "01-30");
157         // Leap year handling
158         validateFreezePeriodsSucceeds("12-01", "02-28");
159         validateFreezePeriodsFailsTooLong("12-01", "03-01");
160 
161         // Regular short separation
162         validateFreezePeriodsFailsTooClose( "01-01", "01-01", "01-03", "01-03");
163         // Short interval spans across end of year
164         validateFreezePeriodsSucceeds("01-31", "03-01", "11-01", "12-01");
165         validateFreezePeriodsFailsTooClose("01-30", "03-01", "11-01", "12-01");
166         // Short separation is after wrapped period
167         validateFreezePeriodsSucceeds("03-03", "03-31", "12-31", "01-01");
168         validateFreezePeriodsFailsTooClose("03-02", "03-31", "12-31", "01-01");
169         // Short separation including Feb 29
170         validateFreezePeriodsSucceeds("12-01", "01-15", "03-17", "04-01");
171         validateFreezePeriodsFailsTooClose("12-01", "01-15", "03-16", "04-01");
172         // Short separation including Feb 29
173         validateFreezePeriodsSucceeds("01-01", "02-28", "04-30", "06-01");
174         validateFreezePeriodsSucceeds("01-01", "02-29", "04-30", "06-01");
175         validateFreezePeriodsFailsTooClose("01-01", "03-01", "04-30", "06-01");
176     }
177 
testFreezePeriodCanBeSetAndChanged()178     public void testFreezePeriodCanBeSetAndChanged() throws Exception {
179         setPolicyWithFreezePeriod("11-02", "01-15", "03-18", "04-30");
180         // Set to a different period should work
181         setPolicyWithFreezePeriod("08-01", "08-30");
182         // Clear freeze period should work
183         setPolicyWithFreezePeriod();
184         // Set to the original period should work
185         setPolicyWithFreezePeriod("11-02", "01-15", "03-18", "04-30");
186     }
187 
testFreezePeriodCannotSetIfTooCloseToPrevious()188     public void testFreezePeriodCannotSetIfTooCloseToPrevious() throws Exception {
189         setSystemDate(LocalDate.of(2018, 2, 28));
190         setPolicyWithFreezePeriod("01-01", "03-01", "06-01", "06-30");
191         // Clear policy
192         mDevicePolicyManager.setSystemUpdatePolicy(getWho(), null);
193         // Set to a conflict period (too close with previous period [2-28, 2-28]) should fail,
194         // despite the previous policy was cleared from the system just now.
195         try {
196             setPolicyWithFreezePeriod("04-29", "04-30");
197             fail("Did no flag invalid period");
198         } catch (ValidationFailedException e) {
199             assertEquals(e.getMessage(),
200                     ValidationFailedException.ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE,
201                     e.getErrorCode());
202         }
203         // This should succeed as the new freeze period is exactly 60 days away.
204         setPolicyWithFreezePeriod("04-30", "04-30");
205     }
206 
testFreezePeriodCannotSetIfTooLongWhenCombinedWithPrevious()207     public void testFreezePeriodCannotSetIfTooLongWhenCombinedWithPrevious() throws Exception {
208         setSystemDate(LocalDate.of(2012, 4, 1));
209         setPolicyWithFreezePeriod("03-01", "05-01");
210         setSystemDate(LocalDate.of(2012, 4, 30));
211         // Despite the wait for broadcast in setSystemDate(), TIME_CHANGED broadcast is asynchronous
212         // so give DevicePolicyManagerService more time to receive TIME_CHANGED and to update the
213         // freeze period record.
214         Thread.sleep(5000);
215         // Set to a conflict period (too long when combined with previous period [04-01, 04-30])
216         // should fail
217         try {
218             setPolicyWithFreezePeriod("04-30", "06-30");
219             fail("Did no flag invalid period");
220         } catch (SystemUpdatePolicy.ValidationFailedException e) {
221             assertEquals(e.getMessage(),
222                     ValidationFailedException.ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG,
223                     e.getErrorCode());
224         }
225         // This should succeed as the combined length (59 days) is just below threshold (90 days).
226         setPolicyWithFreezePeriod("05-01", "06-29");
227     }
228 
testFreezePeriodForOneYear()229     public void testFreezePeriodForOneYear() throws Exception {
230         // Set a normal period every day for 365 days
231         for (int i = 1; i <= 365; i++) {
232             // Add two days so the test date range wraps around year-end
233             setSystemDate(LocalDate.ofYearDay(2019, i).plusDays(2));
234             testFreezePeriodCanBeSetAndChanged();
235         }
236     }
237 
testPolicy(SystemUpdatePolicy policy)238     private void testPolicy(SystemUpdatePolicy policy) {
239         mDevicePolicyManager.setSystemUpdatePolicy(getWho(), policy);
240         waitForPolicyChangedBroadcast();
241         SystemUpdatePolicy newPolicy = mDevicePolicyManager.getSystemUpdatePolicy();
242         if (policy == null) {
243             assertNull(newPolicy);
244         } else {
245             assertNotNull(newPolicy);
246             assertEquals(policy.toString(), newPolicy.toString());
247             assertEquals(policy.getPolicyType(), newPolicy.getPolicyType());
248             if (policy.getPolicyType() == SystemUpdatePolicy.TYPE_INSTALL_WINDOWED) {
249                 assertEquals(policy.getInstallWindowStart(), newPolicy.getInstallWindowStart());
250                 assertEquals(policy.getInstallWindowEnd(), newPolicy.getInstallWindowEnd());
251             }
252         }
253     }
254 
setPolicyWithFreezePeriod(String...dates)255     private void setPolicyWithFreezePeriod(String...dates) {
256         SystemUpdatePolicy policy = SystemUpdatePolicy.createPostponeInstallPolicy();
257         setFreezePeriods(policy, dates);
258         mDevicePolicyManager.setSystemUpdatePolicy(getWho(), policy);
259 
260         List<FreezePeriod> loadedFreezePeriods = mDevicePolicyManager
261                 .getSystemUpdatePolicy().getFreezePeriods();
262         assertEquals(dates.length / 2, loadedFreezePeriods.size());
263         for (int i = 0; i < dates.length; i += 2) {
264             assertEquals(parseMonthDay(dates[i]), loadedFreezePeriods.get(i / 2).getStart());
265             assertEquals(parseMonthDay(dates[i + 1]), loadedFreezePeriods.get(i / 2).getEnd());
266         }
267     }
268 
validateFreezePeriodsSucceeds(String...dates)269     private void validateFreezePeriodsSucceeds(String...dates)  {
270         SystemUpdatePolicy p = SystemUpdatePolicy.createPostponeInstallPolicy();
271         setFreezePeriods(p, dates);
272     }
273 
validateFreezePeriodsFails(int errorCode, String... dates)274     private void validateFreezePeriodsFails(int errorCode, String... dates)  {
275         SystemUpdatePolicy p = SystemUpdatePolicy.createPostponeInstallPolicy();
276         try {
277             setFreezePeriods(p, dates);
278             fail("Exception not thrown for dates: " + String.join(" ", dates));
279         } catch (SystemUpdatePolicy.ValidationFailedException e) {
280             assertEquals("Exception not expected: " + e.getMessage(),
281                     errorCode,e.getErrorCode());
282         }
283     }
284 
validateFreezePeriodsFailsOverlap(String... dates)285     private void validateFreezePeriodsFailsOverlap(String... dates)  {
286         validateFreezePeriodsFails(ValidationFailedException.ERROR_DUPLICATE_OR_OVERLAP, dates);
287     }
288 
validateFreezePeriodsFailsTooLong(String... dates)289     private void validateFreezePeriodsFailsTooLong(String... dates)  {
290         validateFreezePeriodsFails(ValidationFailedException.ERROR_NEW_FREEZE_PERIOD_TOO_LONG,
291                 dates);
292     }
293 
validateFreezePeriodsFailsTooClose(String... dates)294     private void validateFreezePeriodsFailsTooClose(String... dates)  {
295         validateFreezePeriodsFails(ValidationFailedException.ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE,
296                 dates);
297     }
298 
299     //dates are in MM-DD format
setFreezePeriods(SystemUpdatePolicy policy, String... dates)300     private void setFreezePeriods(SystemUpdatePolicy policy, String... dates) {
301         List<FreezePeriod> periods = new ArrayList<>();
302         for (int i = 0; i < dates.length; i+= 2) {
303             periods.add(new FreezePeriod(parseMonthDay(dates[i]), parseMonthDay(dates[i + 1])));
304         }
305         policy.setFreezePeriods(periods);
306     }
307 
parseMonthDay(String date)308     private MonthDay parseMonthDay(String date) {
309         return MonthDay.of(Integer.parseInt(date.substring(0, 2)),
310                 Integer.parseInt(date.substring(3, 5)));
311     }
312 
clearFreezeRecord()313     private void clearFreezeRecord() throws Exception {
314         executeShellCommand("dpm", "clear-freeze-period-record");
315     }
316 
setSystemDate(LocalDate date)317     private void setSystemDate(LocalDate date) throws Exception {
318         mRestoreDate = true;
319         Calendar c = Calendar.getInstance();
320         c.set(Calendar.YEAR, date.getYear());
321         c.set(Calendar.MONTH, date.getMonthValue() - 1);
322         c.set(Calendar.DAY_OF_MONTH, date.getDayOfMonth());
323         mDevicePolicyManager.setTime(getWho(), c.getTimeInMillis());
324         waitForTimeChangedBroadcast();
325     }
326 
waitForPolicyChangedBroadcast()327     private void waitForPolicyChangedBroadcast() {
328         try {
329             assertTrue("Timeout while waiting for broadcast.",
330                     mPolicyChangedSemaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
331         } catch (InterruptedException e) {
332             fail("Interrupted while waiting for broadcast.");
333         }
334     }
335 
waitForTimeChangedBroadcast()336     private void waitForTimeChangedBroadcast() {
337         try {
338             assertTrue("Timeout while waiting for broadcast.",
339                     mTimeChangedSemaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
340         } catch (InterruptedException e) {
341             fail("Interrupted while waiting for broadcast.");
342         }
343     }
344 }
345