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 import static android.provider.Settings.Global.AIRPLANE_MODE_ON; 19 20 import static com.google.common.truth.Truth.assertThat; 21 22 import android.app.admin.DevicePolicyManager; 23 import android.app.admin.FreezePeriod; 24 import android.app.admin.SystemUpdatePolicy; 25 import android.app.admin.SystemUpdatePolicy.ValidationFailedException; 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.icu.util.Calendar; 31 import android.os.Parcel; 32 import android.provider.Settings; 33 import android.provider.Settings.Global; 34 import android.util.Log; 35 import android.util.Pair; 36 import android.provider.Settings; 37 import android.provider.Settings.Global; 38 39 import com.google.common.collect.ImmutableList; 40 import java.time.LocalDate; 41 import java.time.MonthDay; 42 import java.util.ArrayList; 43 import java.util.List; 44 import java.util.Objects; 45 import java.util.concurrent.CountDownLatch; 46 import java.util.concurrent.Semaphore; 47 import java.util.concurrent.TimeUnit; 48 49 /** 50 * Test {@link SystemUpdatePolicy}, {@link DevicePolicyManager#setSystemUpdatePolicy} and 51 * {@link DevicePolicyManager#getSystemUpdatePolicy} 52 */ 53 public class SystemUpdatePolicyTest extends BaseDeviceOwnerTest { 54 55 private static final String TAG = "SystemUpdatePolicyTest"; 56 57 private static final int TIMEOUT_MS = 20_000; 58 private static final int TIMEOUT_SEC = 5; 59 60 private final Semaphore mPolicyChangedSemaphore = new Semaphore(0); 61 private final Semaphore mTimeChangedSemaphore = new Semaphore(0); 62 private final BroadcastReceiver policyChangedReceiver = new BroadcastReceiver() { 63 @Override 64 public void onReceive(Context context, Intent intent) { 65 final String action = intent.getAction(); 66 if (DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED.equals(action)) { 67 mPolicyChangedSemaphore.release(); 68 } else if (Intent.ACTION_TIME_CHANGED.equals(action)) { 69 mTimeChangedSemaphore.release(); 70 } 71 } 72 }; 73 74 private int mSavedAutoTimeConfig; 75 private LocalDate mSavedSystemDate; 76 private boolean mRestoreDate; 77 private int mSavedAirplaneMode; 78 79 @Override 80 protected void setUp() throws Exception { 81 super.setUp(); 82 IntentFilter filter = new IntentFilter(); 83 filter.addAction(DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED); 84 filter.addAction(Intent.ACTION_TIME_CHANGED); 85 mContext.registerReceiver(policyChangedReceiver, filter); 86 clearFreezeRecord(); 87 mSavedAutoTimeConfig = Settings.Global.getInt(mContext.getContentResolver(), 88 Global.AUTO_TIME, 0); 89 executeShellCommand("settings put global auto_time 0"); 90 mSavedSystemDate = LocalDate.now(); 91 mRestoreDate = false; 92 mSavedAirplaneMode = getAirplaneMode(); 93 Log.i(TAG, "Before testing, AIRPLANE_MODE is set to: " + mSavedAirplaneMode); 94 if (mSavedAirplaneMode == 0) { 95 // No need to set mode if AirplaneMode is 1 or error. 96 setAirplaneModeAndWaitBroadcast(1); 97 } 98 } 99 100 @Override 101 protected void tearDown() throws Exception { 102 mDevicePolicyManager.setSystemUpdatePolicy(getWho(), null); 103 clearFreezeRecord(); 104 if (mRestoreDate) { 105 setSystemDate(mSavedSystemDate); 106 } 107 executeShellCommand("settings put global auto_time", 108 Integer.toString(mSavedAutoTimeConfig)); 109 // This needs to happen last since setSystemDate() relies on the receiver for 110 // synchronization. 111 mContext.unregisterReceiver(policyChangedReceiver); 112 if (mSavedAirplaneMode == 0) { 113 // Restore AirplaneMode value. 114 setAirplaneModeAndWaitBroadcast(0); 115 } 116 super.tearDown(); 117 } 118 119 public void testSetEmptytInstallPolicy() { 120 testPolicy(null); 121 } 122 123 public void testSetAutomaticInstallPolicy() { 124 testPolicy(SystemUpdatePolicy.createAutomaticInstallPolicy()); 125 } 126 127 public void testSetWindowedInstallPolicy() { 128 testPolicy(SystemUpdatePolicy.createWindowedInstallPolicy(0, 720)); 129 } 130 131 public void testSetPostponeInstallPolicy() { 132 testPolicy(SystemUpdatePolicy.createPostponeInstallPolicy()); 133 } 134 135 public void testShouldFailInvalidWindowPolicy() throws Exception { 136 try { 137 SystemUpdatePolicy.createWindowedInstallPolicy(24 * 60 + 1, 720); 138 fail("Invalid window start should not be accepted."); 139 } catch (IllegalArgumentException expected) { } 140 try { 141 SystemUpdatePolicy.createWindowedInstallPolicy(-1, 720); 142 fail("Invalid window start should not be accepted."); 143 } catch (IllegalArgumentException expected) { } 144 try { 145 SystemUpdatePolicy.createWindowedInstallPolicy(0, 24 * 60 + 1); 146 fail("Invalid window end should not be accepted."); 147 } catch (IllegalArgumentException expected) { } 148 try { 149 SystemUpdatePolicy.createWindowedInstallPolicy(0, -1); 150 fail("Invalid window end should not be accepted."); 151 } catch (IllegalArgumentException expected) { } 152 } 153 154 public void testFreezePeriodValidation() { 155 // Dates are in MM-DD format 156 validateFreezePeriodsSucceeds("01-01", "01-02"); 157 validateFreezePeriodsSucceeds("01-31", "01-31"); 158 validateFreezePeriodsSucceeds("11-01", "01-15"); 159 validateFreezePeriodsSucceeds("02-01", "02-29"); 160 validateFreezePeriodsSucceeds("03-01", "03-31", "09-01", "09-30"); 161 validateFreezePeriodsSucceeds("10-01", "10-31", "12-31", "01-31"); 162 validateFreezePeriodsSucceeds("01-01", "02-28", "05-01", "06-30", "09-01", "10-31"); 163 validateFreezePeriodsSucceeds("11-02", "01-15", "03-18", "04-30", "08-01", "08-30"); 164 165 // full overlap 166 validateFreezePeriodsFailsOverlap("12-01", "01-31", "12-25", "01-15"); 167 // partial overlap 168 validateFreezePeriodsFailsOverlap("03-01", "03-31", "03-15", "01-01"); 169 // touching interval 170 validateFreezePeriodsFailsOverlap("01-31", "01-31", "02-01", "02-01"); 171 validateFreezePeriodsFailsOverlap("12-01", "12-31", "04-01", "04-01", "01-01", "01-30"); 172 173 // entire year 174 validateFreezePeriodsFailsTooLong("01-01", "12-31"); 175 // Regular long period 176 validateFreezePeriodsSucceeds("01-01", "03-31", "06-01", "08-29"); 177 validateFreezePeriodsFailsTooLong("01-01", "03-31", "06-01", "08-30"); 178 // long period spanning across year end 179 validateFreezePeriodsSucceeds("11-01", "01-29"); 180 validateFreezePeriodsFailsTooLong("11-01", "01-30"); 181 // Leap year handling 182 validateFreezePeriodsSucceeds("12-01", "02-28"); 183 validateFreezePeriodsFailsTooLong("12-01", "03-01"); 184 185 // Regular short separation 186 validateFreezePeriodsFailsTooClose( "01-01", "01-01", "01-03", "01-03"); 187 // Short interval spans across end of year 188 validateFreezePeriodsSucceeds("01-31", "03-01", "11-01", "12-01"); 189 validateFreezePeriodsFailsTooClose("01-30", "03-01", "11-01", "12-01"); 190 // Short separation is after wrapped period 191 validateFreezePeriodsSucceeds("03-03", "03-31", "12-31", "01-01"); 192 validateFreezePeriodsFailsTooClose("03-02", "03-31", "12-31", "01-01"); 193 // Short separation including Feb 29 194 validateFreezePeriodsSucceeds("12-01", "01-15", "03-17", "04-01"); 195 validateFreezePeriodsFailsTooClose("12-01", "01-15", "03-16", "04-01"); 196 // Short separation including Feb 29 197 validateFreezePeriodsSucceeds("01-01", "02-28", "04-30", "06-01"); 198 validateFreezePeriodsSucceeds("01-01", "02-29", "04-30", "06-01"); 199 validateFreezePeriodsFailsTooClose("01-01", "03-01", "04-30", "06-01"); 200 } 201 202 public void testFreezePeriodCanBeSetAndChanged() throws Exception { 203 setPolicyWithFreezePeriod("11-02", "01-15", "03-18", "04-30"); 204 // Set to a different period should work 205 setPolicyWithFreezePeriod("08-01", "08-30"); 206 // Clear freeze period should work 207 setPolicyWithFreezePeriod(); 208 // Set to the original period should work 209 setPolicyWithFreezePeriod("11-02", "01-15", "03-18", "04-30"); 210 } 211 212 public void testFreezePeriodCannotSetIfTooCloseToPrevious() throws Exception { 213 setSystemDate(LocalDate.of(2018, 2, 28)); 214 setPolicyWithFreezePeriod("01-01", "03-01", "06-01", "06-30"); 215 // Clear policy 216 mDevicePolicyManager.setSystemUpdatePolicy(getWho(), null); 217 // Set to a conflict period (too close with previous period [2-28, 2-28]) should fail, 218 // despite the previous policy was cleared from the system just now. 219 try { 220 setPolicyWithFreezePeriod("04-29", "04-30"); 221 fail("Did no flag invalid period"); 222 } catch (ValidationFailedException e) { 223 assertEquals(e.getMessage(), 224 ValidationFailedException.ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE, 225 e.getErrorCode()); 226 } 227 // This should succeed as the new freeze period is exactly 60 days away. 228 setPolicyWithFreezePeriod("04-30", "04-30"); 229 } 230 231 public void testFreezePeriodCannotSetIfTooLongWhenCombinedWithPrevious() throws Exception { 232 setSystemDate(LocalDate.of(2012, 4, 1)); 233 setPolicyWithFreezePeriod("03-01", "05-01"); 234 setSystemDate(LocalDate.of(2012, 4, 30)); 235 // Despite the wait for broadcast in setSystemDate(), TIME_CHANGED broadcast is asynchronous 236 // so give DevicePolicyManagerService more time to receive TIME_CHANGED and to update the 237 // freeze period record. 238 Thread.sleep(5000); 239 // Set to a conflict period (too long when combined with previous period [04-01, 04-30]) 240 // should fail 241 try { 242 setPolicyWithFreezePeriod("04-30", "06-30"); 243 fail("Did no flag invalid period"); 244 } catch (SystemUpdatePolicy.ValidationFailedException e) { 245 assertEquals(e.getMessage(), 246 ValidationFailedException.ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG, 247 e.getErrorCode()); 248 } 249 // This should succeed as the combined length (59 days) is just below threshold (90 days). 250 setPolicyWithFreezePeriod("05-01", "06-29"); 251 } 252 253 public void testFreezePeriodForOneYear() throws Exception { 254 // Set a normal period every day for 365 days 255 for (int i = 1; i <= 365; i++) { 256 // Add two days so the test date range wraps around year-end 257 setSystemDate(LocalDate.ofYearDay(2019, i).plusDays(2)); 258 testFreezePeriodCanBeSetAndChanged(); 259 } 260 } 261 262 public void testWriteSystemUpdatePolicyToParcel() { 263 final Parcel parcel1 = Parcel.obtain(); 264 try { 265 final SystemUpdatePolicy policy1 = SystemUpdatePolicy.createAutomaticInstallPolicy(); 266 policy1.writeToParcel(parcel1, 0); 267 parcel1.setDataPosition(0); 268 final SystemUpdatePolicy copy1 = SystemUpdatePolicy.CREATOR.createFromParcel(parcel1); 269 assertThat(copy1).isNotNull(); 270 assertSystemUpdatePoliciesEqual(policy1, copy1); 271 } finally { 272 parcel1.recycle(); 273 } 274 275 final Parcel parcel2 = Parcel.obtain(); 276 try { 277 final SystemUpdatePolicy policy2 = SystemUpdatePolicy 278 .createWindowedInstallPolicy(0, 720); 279 policy2.writeToParcel(parcel2, 0); 280 parcel2.setDataPosition(0); 281 final SystemUpdatePolicy copy2 = SystemUpdatePolicy.CREATOR.createFromParcel(parcel2); 282 assertThat(copy2).isNotNull(); 283 assertSystemUpdatePoliciesEqual(policy2, copy2); 284 } finally { 285 parcel2.recycle(); 286 } 287 288 final Parcel parcel3 = Parcel.obtain(); 289 try { 290 final SystemUpdatePolicy policy3 = SystemUpdatePolicy.createPostponeInstallPolicy(); 291 policy3.writeToParcel(parcel3, 0); 292 parcel3.setDataPosition(0); 293 final SystemUpdatePolicy copy3 = SystemUpdatePolicy.CREATOR.createFromParcel(parcel3); 294 assertThat(copy3).isNotNull(); 295 assertSystemUpdatePoliciesEqual(policy3, copy3); 296 } finally { 297 parcel3.recycle(); 298 } 299 } 300 301 public void testWriteValidationFailedExceptionToParcel() { 302 final List<FreezePeriod> freezePeriods = 303 ImmutableList.of(new FreezePeriod(MonthDay.of(1, 10), MonthDay.of(1, 9))); 304 try { 305 SystemUpdatePolicy.createAutomaticInstallPolicy().setFreezePeriods(freezePeriods); 306 fail("ValidationFailedException not thrown for invalid freeze period."); 307 } catch (ValidationFailedException e) { 308 final Parcel parcel = Parcel.obtain(); 309 e.writeToParcel(parcel, 0); 310 parcel.setDataPosition(0); 311 312 final ValidationFailedException copy = 313 ValidationFailedException.CREATOR.createFromParcel(parcel); 314 315 assertThat(copy).isNotNull(); 316 assertThat(e.getErrorCode()).isEqualTo(copy.getErrorCode()); 317 assertThat(e.getMessage()).isEqualTo(copy.getMessage()); 318 } 319 } 320 321 private void assertSystemUpdatePoliciesEqual(SystemUpdatePolicy policy, 322 SystemUpdatePolicy copy) { 323 assertThat(policy.getInstallWindowStart()).isEqualTo(copy.getInstallWindowStart()); 324 assertThat(policy.getInstallWindowEnd()).isEqualTo(copy.getInstallWindowEnd()); 325 assertFreezePeriodListsEqual(policy.getFreezePeriods(), copy.getFreezePeriods()); 326 assertThat(policy.getPolicyType()).isEqualTo(copy.getPolicyType()); 327 } 328 329 private void assertFreezePeriodListsEqual(List<FreezePeriod> original, 330 List<FreezePeriod> copy) { 331 assertThat(original).isNotNull(); 332 assertThat(copy).isNotNull(); 333 assertThat(original.size()).isEqualTo(copy.size()); 334 for (FreezePeriod period1 : original) { 335 assertThat(period1).isNotNull(); 336 assertFreezePeriodListContains(copy, period1); 337 } 338 for (FreezePeriod period1 : copy) { 339 assertThat(period1).isNotNull(); 340 assertFreezePeriodListContains(original, period1); 341 } 342 } 343 344 private void assertFreezePeriodListContains(List<FreezePeriod> list, FreezePeriod period) { 345 for (FreezePeriod other : list) { 346 assertThat(other).isNotNull(); 347 if (areFreezePeriodsEqual(period, other)) { 348 return; 349 } 350 } 351 final List<String> printablePeriods = new ArrayList<>(); 352 for (FreezePeriod printablePeriod : list) { 353 printablePeriods.add(printablePeriod.toString()); 354 } 355 fail(String.format("FreezePeriod list [%s] does not contain the specified period %s.", 356 String.join(", ", printablePeriods), period)); 357 } 358 359 private boolean areFreezePeriodsEqual(FreezePeriod period1, FreezePeriod period2) { 360 return period1 != null && period2 != null 361 && Objects.equals(period1.getStart(), period2.getStart()) 362 && Objects.equals(period1.getEnd(), period2.getEnd()); 363 } 364 365 private void testPolicy(SystemUpdatePolicy policy) { 366 mDevicePolicyManager.setSystemUpdatePolicy(getWho(), policy); 367 waitForPolicyChangedBroadcast(); 368 SystemUpdatePolicy newPolicy = mDevicePolicyManager.getSystemUpdatePolicy(); 369 if (policy == null) { 370 assertNull(newPolicy); 371 } else { 372 assertNotNull(newPolicy); 373 assertEquals(policy.toString(), newPolicy.toString()); 374 assertEquals(policy.getPolicyType(), newPolicy.getPolicyType()); 375 if (policy.getPolicyType() == SystemUpdatePolicy.TYPE_INSTALL_WINDOWED) { 376 assertEquals(policy.getInstallWindowStart(), newPolicy.getInstallWindowStart()); 377 assertEquals(policy.getInstallWindowEnd(), newPolicy.getInstallWindowEnd()); 378 } 379 } 380 } 381 382 private void setPolicyWithFreezePeriod(String...dates) { 383 SystemUpdatePolicy policy = SystemUpdatePolicy.createPostponeInstallPolicy(); 384 setFreezePeriods(policy, dates); 385 mDevicePolicyManager.setSystemUpdatePolicy(getWho(), policy); 386 387 List<FreezePeriod> loadedFreezePeriods = mDevicePolicyManager 388 .getSystemUpdatePolicy().getFreezePeriods(); 389 assertEquals(dates.length / 2, loadedFreezePeriods.size()); 390 for (int i = 0; i < dates.length; i += 2) { 391 assertEquals(parseMonthDay(dates[i]), loadedFreezePeriods.get(i / 2).getStart()); 392 assertEquals(parseMonthDay(dates[i + 1]), loadedFreezePeriods.get(i / 2).getEnd()); 393 } 394 } 395 396 private void validateFreezePeriodsSucceeds(String...dates) { 397 SystemUpdatePolicy p = SystemUpdatePolicy.createPostponeInstallPolicy(); 398 setFreezePeriods(p, dates); 399 } 400 401 private void validateFreezePeriodsFails(int errorCode, String... dates) { 402 SystemUpdatePolicy p = SystemUpdatePolicy.createPostponeInstallPolicy(); 403 try { 404 setFreezePeriods(p, dates); 405 fail("Exception not thrown for dates: " + String.join(" ", dates)); 406 } catch (SystemUpdatePolicy.ValidationFailedException e) { 407 assertEquals("Exception not expected: " + e.getMessage(), 408 errorCode,e.getErrorCode()); 409 } 410 } 411 412 private void validateFreezePeriodsFailsOverlap(String... dates) { 413 validateFreezePeriodsFails(ValidationFailedException.ERROR_DUPLICATE_OR_OVERLAP, dates); 414 } 415 416 private void validateFreezePeriodsFailsTooLong(String... dates) { 417 validateFreezePeriodsFails(ValidationFailedException.ERROR_NEW_FREEZE_PERIOD_TOO_LONG, 418 dates); 419 } 420 421 private void validateFreezePeriodsFailsTooClose(String... dates) { 422 validateFreezePeriodsFails(ValidationFailedException.ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE, 423 dates); 424 } 425 426 //dates are in MM-DD format 427 private void setFreezePeriods(SystemUpdatePolicy policy, String... dates) { 428 List<FreezePeriod> periods = new ArrayList<>(); 429 for (int i = 0; i < dates.length; i+= 2) { 430 periods.add(new FreezePeriod(parseMonthDay(dates[i]), parseMonthDay(dates[i + 1]))); 431 } 432 policy.setFreezePeriods(periods); 433 } 434 435 private MonthDay parseMonthDay(String date) { 436 return MonthDay.of(Integer.parseInt(date.substring(0, 2)), 437 Integer.parseInt(date.substring(3, 5))); 438 } 439 440 private void clearFreezeRecord() throws Exception { 441 executeShellCommand("dpm", "clear-freeze-period-record"); 442 } 443 444 private void setSystemDate(LocalDate date) throws Exception { 445 mRestoreDate = true; 446 Calendar c = Calendar.getInstance(); 447 c.set(Calendar.YEAR, date.getYear()); 448 c.set(Calendar.MONTH, date.getMonthValue() - 1); 449 c.set(Calendar.DAY_OF_MONTH, date.getDayOfMonth()); 450 mDevicePolicyManager.setTime(getWho(), c.getTimeInMillis()); 451 waitForTimeChangedBroadcast(); 452 } 453 454 private void waitForPolicyChangedBroadcast() { 455 try { 456 assertTrue("Timeout while waiting for broadcast.", 457 mPolicyChangedSemaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 458 } catch (InterruptedException e) { 459 fail("Interrupted while waiting for broadcast."); 460 } 461 } 462 463 private void waitForTimeChangedBroadcast() { 464 try { 465 assertTrue("Timeout while waiting for broadcast.", 466 mTimeChangedSemaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 467 } catch (InterruptedException e) { 468 fail("Interrupted while waiting for broadcast."); 469 } 470 } 471 472 private int getAirplaneMode() throws Settings.SettingNotFoundException { 473 int airplaneMode = 0xFF; 474 try { 475 airplaneMode = Settings.Global.getInt(mContext.getContentResolver(), 476 Settings.Global.AIRPLANE_MODE_ON); 477 } catch (Settings.SettingNotFoundException e) { 478 airplaneMode = 0xFF; 479 // if the mode is not supported, return a non zero value. 480 Log.i(TAG, "Airplane mode is not found in Settings. Skipping AirplaneMode update"); 481 } finally { 482 return airplaneMode; 483 } 484 } 485 486 private boolean setAirplaneModeAndWaitBroadcast (int state) throws Exception { 487 Log.i(TAG, "setAirplaneModeAndWaitBroadcast setting state(0=disable, 1=enable): " + state); 488 489 final CountDownLatch latch = new CountDownLatch(1); 490 BroadcastReceiver receiver = new BroadcastReceiver() { 491 @Override 492 public void onReceive(Context context, Intent intent) { 493 Log.i(TAG, "Received broadcast for AirplaneModeUpdate"); 494 latch.countDown(); 495 } 496 }; 497 mContext.registerReceiver(receiver, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); 498 try { 499 Settings.Global.putInt(mContext.getContentResolver(), AIRPLANE_MODE_ON, state); 500 if (!latch.await(TIMEOUT_SEC, TimeUnit.SECONDS)) { 501 Log.d(TAG, "Failed to receive broadcast in " + TIMEOUT_SEC + "sec"); 502 return false; 503 } 504 } finally { 505 mContext.unregisterReceiver(receiver); 506 } 507 return true; 508 } 509 } 510