1 /* 2 * Copyright 2017 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 17 package com.android.internal.telephony; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.assertTrue; 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.ArgumentMatchers.anyLong; 25 import static org.mockito.Mockito.atLeast; 26 import static org.mockito.Mockito.clearInvocations; 27 import static org.mockito.Mockito.times; 28 import static org.mockito.Mockito.verify; 29 import static org.mockito.Mockito.verifyNoMoreInteractions; 30 import static org.mockito.Mockito.when; 31 32 import android.icu.util.Calendar; 33 import android.icu.util.GregorianCalendar; 34 import android.icu.util.TimeZone; 35 36 import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult; 37 import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult; 38 import com.android.internal.telephony.util.TimeStampedValue; 39 40 import org.junit.After; 41 import org.junit.Before; 42 import org.junit.Test; 43 import org.mockito.ArgumentCaptor; 44 import org.mockito.Mock; 45 46 public class NitzStateMachineTest extends TelephonyTest { 47 48 @Mock 49 private NitzStateMachine.DeviceState mDeviceState; 50 51 @Mock 52 private TimeServiceHelper mTimeServiceHelper; 53 54 private TimeZoneLookupHelper mRealTimeZoneLookupHelper; 55 56 private NitzStateMachine mNitzStateMachine; 57 58 @Before 59 public void setUp() throws Exception { 60 logd("NitzStateMachineTest +Setup!"); 61 super.setUp("NitzStateMachineTest"); 62 63 // In tests we use the real TimeZoneLookupHelper. 64 mRealTimeZoneLookupHelper = new TimeZoneLookupHelper(); 65 mNitzStateMachine = new NitzStateMachine( 66 mPhone, mTimeServiceHelper, mDeviceState, mRealTimeZoneLookupHelper); 67 68 logd("ServiceStateTrackerTest -Setup!"); 69 } 70 71 @After 72 public void tearDown() throws Exception { 73 checkNoUnverifiedSetOperations(mTimeServiceHelper); 74 75 super.tearDown(); 76 } 77 78 // A country that has multiple zones, but there is only one matching time zone at the time : 79 // the zone cannot be guessed from the country alone, but can be guessed from the country + 80 // NITZ. 81 private static final Scenario UNIQUE_US_ZONE_SCENARIO = new Scenario.Builder() 82 .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0) 83 .setInitialDeviceRealtimeMillis(123456789L) 84 .setTimeZone("America/Los_Angeles") 85 .setActualTimeUtc(2018, 1, 1, 12, 0, 0) 86 .setCountryIso("us") 87 .build(); 88 89 @Test 90 public void test_uniqueUsZone_Assumptions() { 91 // Check we'll get the expected behavior from TimeZoneLookupHelper. 92 93 // allZonesHaveSameOffset == false, so we shouldn't pick an arbitrary zone. 94 CountryResult expectedCountryLookupResult = new CountryResult( 95 "America/New_York", false /* allZonesHaveSameOffset */, 96 UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis()); 97 CountryResult actualCountryLookupResult = 98 mRealTimeZoneLookupHelper.lookupByCountry( 99 UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode(), 100 UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis()); 101 assertEquals(expectedCountryLookupResult, actualCountryLookupResult); 102 103 // isOnlyMatch == true, so the combination of country + NITZ should be enough. 104 OffsetResult expectedLookupResult = 105 new OffsetResult("America/Los_Angeles", true /* isOnlyMatch */); 106 OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry( 107 UNIQUE_US_ZONE_SCENARIO.getNitzSignal().mValue, 108 UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode()); 109 assertEquals(expectedLookupResult, actualLookupResult); 110 } 111 112 // A country with a single zone : the zone can be guessed from the country. 113 private static final Scenario UNITED_KINGDOM_SCENARIO = new Scenario.Builder() 114 .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0) 115 .setInitialDeviceRealtimeMillis(123456789L) 116 .setTimeZone("Europe/London") 117 .setActualTimeUtc(2018, 1, 1, 12, 0, 0) 118 .setCountryIso("gb") 119 .build(); 120 121 @Test 122 public void test_unitedKingdom_Assumptions() { 123 // Check we'll get the expected behavior from TimeZoneLookupHelper. 124 125 // allZonesHaveSameOffset == true (not only that, there is only one zone), so we can pick 126 // the zone knowing only the country. 127 CountryResult expectedCountryLookupResult = new CountryResult( 128 "Europe/London", true /* allZonesHaveSameOffset */, 129 UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis()); 130 CountryResult actualCountryLookupResult = 131 mRealTimeZoneLookupHelper.lookupByCountry( 132 UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode(), 133 UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis()); 134 assertEquals(expectedCountryLookupResult, actualCountryLookupResult); 135 136 OffsetResult expectedLookupResult = 137 new OffsetResult("Europe/London", true /* isOnlyMatch */); 138 OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry( 139 UNITED_KINGDOM_SCENARIO.getNitzSignal().mValue, 140 UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode()); 141 assertEquals(expectedLookupResult, actualLookupResult); 142 } 143 144 @Test 145 public void test_uniqueUsZone_timeEnabledTimeZoneEnabled_countryThenNitz() throws Exception { 146 Scenario scenario = UNIQUE_US_ZONE_SCENARIO; 147 Device device = new DeviceBuilder() 148 .setClocksFromScenario(scenario) 149 .setTimeDetectionEnabled(true) 150 .setTimeZoneDetectionEnabled(true) 151 .setTimeZoneSettingInitialized(false) 152 .initialize(); 153 Script script = new Script(device); 154 155 int clockIncrement = 1250; 156 long expectedTimeMillis = scenario.getActualTimeMillis() + clockIncrement; 157 script.countryReceived(scenario.getNetworkCountryIsoCode()) 158 // Country won't be enough for time zone detection. 159 .verifyNothingWasSetAndReset() 160 // Increment the clock so we can tell the time was adjusted correctly when set. 161 .incrementClocks(clockIncrement) 162 .nitzReceived(scenario.getNitzSignal()) 163 // Country + NITZ is enough for both time + time zone detection. 164 .verifyTimeAndZoneSetAndReset(expectedTimeMillis, scenario.getTimeZoneId()); 165 166 // Check NitzStateMachine state. 167 assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); 168 assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData()); 169 assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); 170 } 171 172 @Test 173 public void test_unitedKingdom_timeEnabledTimeZoneEnabled_countryThenNitz() throws Exception { 174 Scenario scenario = UNITED_KINGDOM_SCENARIO; 175 Device device = new DeviceBuilder() 176 .setClocksFromScenario(scenario) 177 .setTimeDetectionEnabled(true) 178 .setTimeZoneDetectionEnabled(true) 179 .setTimeZoneSettingInitialized(false) 180 .initialize(); 181 Script script = new Script(device); 182 183 int clockIncrement = 1250; 184 long expectedTimeMillis = scenario.getActualTimeMillis() + clockIncrement; 185 script.countryReceived(scenario.getNetworkCountryIsoCode()) 186 // Country alone is enough to guess the time zone. 187 .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId()) 188 // Increment the clock so we can tell the time was adjusted correctly when set. 189 .incrementClocks(clockIncrement) 190 .nitzReceived(scenario.getNitzSignal()) 191 // Country + NITZ is enough for both time + time zone detection. 192 .verifyTimeAndZoneSetAndReset(expectedTimeMillis, scenario.getTimeZoneId()); 193 194 // Check NitzStateMachine state. 195 assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); 196 assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData()); 197 assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); 198 } 199 200 @Test 201 public void test_uniqueUsZone_timeEnabledTimeZoneDisabled_countryThenNitz() throws Exception { 202 Scenario scenario = UNIQUE_US_ZONE_SCENARIO; 203 Device device = new DeviceBuilder() 204 .setClocksFromScenario(scenario) 205 .setTimeDetectionEnabled(true) 206 .setTimeZoneDetectionEnabled(false) 207 .setTimeZoneSettingInitialized(false) 208 .initialize(); 209 Script script = new Script(device); 210 211 int clockIncrement = 1250; 212 script.countryReceived(scenario.getNetworkCountryIsoCode()) 213 // Country is not enough to guess the time zone and time zone detection is disabled. 214 .verifyNothingWasSetAndReset() 215 // Increment the clock so we can tell the time was adjusted correctly when set. 216 .incrementClocks(clockIncrement) 217 .nitzReceived(scenario.getNitzSignal()) 218 // Time zone detection is disabled, but time should be set from NITZ. 219 .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis() + clockIncrement); 220 221 // Check NitzStateMachine state. 222 assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); 223 assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData()); 224 assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); 225 } 226 227 @Test 228 public void test_unitedKingdom_timeEnabledTimeZoneDisabled_countryThenNitz() 229 throws Exception { 230 Scenario scenario = UNITED_KINGDOM_SCENARIO; 231 Device device = new DeviceBuilder() 232 .setClocksFromScenario(scenario) 233 .setTimeDetectionEnabled(true) 234 .setTimeZoneDetectionEnabled(false) 235 .setTimeZoneSettingInitialized(false) 236 .initialize(); 237 Script script = new Script(device); 238 239 int clockIncrement = 1250; 240 script.countryReceived(scenario.getNetworkCountryIsoCode()) 241 // Country alone would be enough for time zone detection, but it's disabled. 242 .verifyNothingWasSetAndReset() 243 // Increment the clock so we can tell the time was adjusted correctly when set. 244 .incrementClocks(clockIncrement) 245 .nitzReceived(scenario.getNitzSignal()) 246 // Time zone detection is disabled, but time should be set from NITZ. 247 .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis() + clockIncrement); 248 249 // Check NitzStateMachine state. 250 assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); 251 assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData()); 252 assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); 253 } 254 255 @Test 256 public void test_uniqueUsZone_timeDisabledTimeZoneEnabled_countryThenNitz() throws Exception { 257 Scenario scenario = UNIQUE_US_ZONE_SCENARIO; 258 Device device = new DeviceBuilder() 259 .setClocksFromScenario(scenario) 260 .setTimeDetectionEnabled(false) 261 .setTimeZoneDetectionEnabled(true) 262 .setTimeZoneSettingInitialized(false) 263 .initialize(); 264 Script script = new Script(device); 265 266 script.countryReceived(scenario.getNetworkCountryIsoCode()) 267 // Country won't be enough for time zone detection. 268 .verifyNothingWasSetAndReset() 269 .nitzReceived(scenario.getNitzSignal()) 270 // Time detection is disabled, but time zone should be detected from country + NITZ. 271 .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId()); 272 273 // Check NitzStateMachine state. 274 assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); 275 assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData()); 276 assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); 277 } 278 279 @Test 280 public void test_unitedKingdom_timeDisabledTimeZoneEnabled_countryThenNitz() throws Exception { 281 Scenario scenario = UNITED_KINGDOM_SCENARIO; 282 Device device = new DeviceBuilder() 283 .setClocksFromScenario(scenario) 284 .setTimeDetectionEnabled(false) 285 .setTimeZoneDetectionEnabled(true) 286 .setTimeZoneSettingInitialized(false) 287 .initialize(); 288 Script script = new Script(device); 289 290 script.countryReceived(scenario.getNetworkCountryIsoCode()) 291 // Country alone is enough to detect time zone. 292 .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId()) 293 .nitzReceived(scenario.getNitzSignal()) 294 // Time detection is disabled, so we don't set the clock from NITZ. 295 .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId()); 296 297 // Check NitzStateMachine state. 298 assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); 299 assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData()); 300 assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); 301 } 302 303 @Test 304 public void test_uniqueUsZone_timeDisabledTimeZoneDisabled_countryThenNitz() throws Exception { 305 Scenario scenario = UNIQUE_US_ZONE_SCENARIO; 306 Device device = new DeviceBuilder() 307 .setClocksFromScenario(scenario) 308 .setTimeDetectionEnabled(false) 309 .setTimeZoneDetectionEnabled(false) 310 .setTimeZoneSettingInitialized(false) 311 .initialize(); 312 Script script = new Script(device); 313 314 script.countryReceived(scenario.getNetworkCountryIsoCode()) 315 // Time and time zone detection is disabled. 316 .verifyNothingWasSetAndReset() 317 .nitzReceived(scenario.getNitzSignal()) 318 // Time and time zone detection is disabled. 319 .verifyNothingWasSetAndReset(); 320 321 // Check NitzStateMachine state. 322 assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); 323 assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData()); 324 assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); 325 } 326 327 @Test 328 public void test_unitedKingdom_timeDisabledTimeZoneDisabled_countryThenNitz() throws Exception { 329 Scenario scenario = UNITED_KINGDOM_SCENARIO; 330 Device device = new DeviceBuilder() 331 .setClocksFromScenario(scenario) 332 .setTimeDetectionEnabled(false) 333 .setTimeZoneDetectionEnabled(false) 334 .setTimeZoneSettingInitialized(false) 335 .initialize(); 336 Script script = new Script(device); 337 338 script.countryReceived(scenario.getNetworkCountryIsoCode()) 339 // Time and time zone detection is disabled. 340 .verifyNothingWasSetAndReset() 341 .nitzReceived(scenario.getNitzSignal()) 342 // Time and time zone detection is disabled. 343 .verifyNothingWasSetAndReset(); 344 345 // Check NitzStateMachine state. 346 assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); 347 assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData()); 348 assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); 349 } 350 351 @Test 352 public void test_uniqueUsZone_timeDisabledTimeZoneEnabled_nitzThenCountry() throws Exception { 353 Scenario scenario = UNIQUE_US_ZONE_SCENARIO; 354 Device device = new DeviceBuilder() 355 .setClocksFromScenario(scenario) 356 .setTimeDetectionEnabled(false) 357 .setTimeZoneDetectionEnabled(true) 358 .setTimeZoneSettingInitialized(false) 359 .initialize(); 360 Script script = new Script(device); 361 362 // Simulate receiving an NITZ signal. 363 script.nitzReceived(scenario.getNitzSignal()) 364 // The NITZ alone isn't enough to detect a time zone. 365 .verifyNothingWasSetAndReset(); 366 367 // Check NitzStateMachine state. 368 assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); 369 assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData()); 370 assertNull(mNitzStateMachine.getSavedTimeZoneId()); 371 372 // Simulate the country code becoming known. 373 script.countryReceived(scenario.getNetworkCountryIsoCode()) 374 // The NITZ + country is enough to detect the time zone. 375 .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId()); 376 377 // Check NitzStateMachine state. 378 // TODO(nfuller): The following line should probably be assertTrue but the logic under test 379 // may be buggy. Look at whether it needs to change. 380 assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); 381 assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData()); 382 assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); 383 } 384 385 @Test 386 public void test_unitedKingdom_timeDisabledTimeZoneEnabled_nitzThenCountry() throws Exception { 387 Scenario scenario = UNITED_KINGDOM_SCENARIO; 388 Device device = new DeviceBuilder() 389 .setClocksFromScenario(scenario) 390 .setTimeDetectionEnabled(false) 391 .setTimeZoneDetectionEnabled(true) 392 .setTimeZoneSettingInitialized(false) 393 .initialize(); 394 Script script = new Script(device); 395 396 // Simulate receiving an NITZ signal. 397 script.nitzReceived(scenario.getNitzSignal()) 398 // The NITZ alone isn't enough to detect a time zone. 399 .verifyNothingWasSetAndReset(); 400 401 // Check NitzStateMachine state. 402 assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); 403 assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData()); 404 assertNull(mNitzStateMachine.getSavedTimeZoneId()); 405 406 // Simulate the country code becoming known. 407 script.countryReceived(scenario.getNetworkCountryIsoCode()); 408 409 // The NITZ + country is enough to detect the time zone. 410 // NOTE: setting the timezone happens twice because of a quirk in NitzStateMachine: it 411 // handles the country lookup / set, then combines the country with the NITZ state and does 412 // another lookup / set. We shouldn't require it is set twice but we do for simplicity. 413 script.verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2 /* times */); 414 415 // Check NitzStateMachine state. 416 // TODO(nfuller): The following line should probably be assertTrue but the logic under test 417 // may be buggy. Look at whether it needs to change. 418 assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); 419 assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData()); 420 assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); 421 } 422 423 private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute, 424 int second) { 425 Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC")); 426 cal.clear(); 427 cal.set(year, monthInYear - 1, day, hourOfDay, minute, second); 428 return cal.getTimeInMillis(); 429 } 430 431 /** 432 * A helper class for common test operations involving a device. 433 */ 434 class Script { 435 private final Device mDevice; 436 437 Script(Device device) { 438 this.mDevice = device; 439 } 440 441 Script countryReceived(String countryIsoCode) { 442 mDevice.networkCountryKnown(countryIsoCode); 443 return this; 444 } 445 446 Script nitzReceived(TimeStampedValue<NitzData> nitzSignal) { 447 mDevice.nitzSignalReceived(nitzSignal); 448 return this; 449 } 450 451 Script incrementClocks(int clockIncrement) { 452 mDevice.incrementClocks(clockIncrement); 453 return this; 454 } 455 456 Script verifyNothingWasSetAndReset() { 457 mDevice.verifyTimeZoneWasNotSet(); 458 mDevice.verifyTimeWasNotSet(); 459 mDevice.checkNoUnverifiedSetOperations(); 460 mDevice.resetInvocations(); 461 return this; 462 } 463 464 Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId, int times) { 465 mDevice.verifyTimeZoneWasSet(timeZoneId, times); 466 mDevice.verifyTimeWasNotSet(); 467 mDevice.checkNoUnverifiedSetOperations(); 468 mDevice.resetInvocations(); 469 return this; 470 } 471 472 Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId) { 473 return verifyOnlyTimeZoneWasSetAndReset(timeZoneId, 1); 474 } 475 476 Script verifyOnlyTimeWasSetAndReset(long expectedTimeMillis) { 477 mDevice.verifyTimeZoneWasNotSet(); 478 mDevice.verifyTimeWasSet(expectedTimeMillis); 479 mDevice.checkNoUnverifiedSetOperations(); 480 mDevice.resetInvocations(); 481 return this; 482 } 483 484 Script verifyTimeAndZoneSetAndReset(long expectedTimeMillis, String timeZoneId) { 485 mDevice.verifyTimeZoneWasSet(timeZoneId); 486 mDevice.verifyTimeWasSet(expectedTimeMillis); 487 mDevice.checkNoUnverifiedSetOperations(); 488 mDevice.resetInvocations(); 489 return this; 490 } 491 492 Script reset() { 493 mDevice.checkNoUnverifiedSetOperations(); 494 mDevice.resetInvocations(); 495 return this; 496 } 497 } 498 499 /** 500 * An abstraction of a device for use in telephony time zone detection tests. It can be used to 501 * retrieve device state, modify device state and verify changes. 502 */ 503 class Device { 504 505 private final long mInitialSystemClockMillis; 506 private final long mInitialRealtimeMillis; 507 private final boolean mTimeDetectionEnabled; 508 private final boolean mTimeZoneDetectionEnabled; 509 private final boolean mTimeZoneSettingInitialized; 510 511 Device(long initialSystemClockMillis, long initialRealtimeMillis, 512 boolean timeDetectionEnabled, boolean timeZoneDetectionEnabled, 513 boolean timeZoneSettingInitialized) { 514 mInitialSystemClockMillis = initialSystemClockMillis; 515 mInitialRealtimeMillis = initialRealtimeMillis; 516 mTimeDetectionEnabled = timeDetectionEnabled; 517 mTimeZoneDetectionEnabled = timeZoneDetectionEnabled; 518 mTimeZoneSettingInitialized = timeZoneSettingInitialized; 519 } 520 521 void initialize() { 522 // Set initial configuration. 523 when(mDeviceState.getIgnoreNitz()).thenReturn(false); 524 when(mDeviceState.getNitzUpdateDiffMillis()).thenReturn(2000); 525 when(mDeviceState.getNitzUpdateSpacingMillis()).thenReturn(1000 * 60 * 10); 526 527 // Simulate the country not being known. 528 when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn(""); 529 530 when(mTimeServiceHelper.elapsedRealtime()).thenReturn(mInitialRealtimeMillis); 531 when(mTimeServiceHelper.currentTimeMillis()).thenReturn(mInitialSystemClockMillis); 532 when(mTimeServiceHelper.isTimeDetectionEnabled()).thenReturn(mTimeDetectionEnabled); 533 when(mTimeServiceHelper.isTimeZoneDetectionEnabled()) 534 .thenReturn(mTimeZoneDetectionEnabled); 535 when(mTimeServiceHelper.isTimeZoneSettingInitialized()) 536 .thenReturn(mTimeZoneSettingInitialized); 537 } 538 539 void networkCountryKnown(String countryIsoCode) { 540 when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn(countryIsoCode); 541 mNitzStateMachine.handleNetworkCountryCodeSet(true); 542 } 543 544 void incrementClocks(int millis) { 545 long currentElapsedRealtime = mTimeServiceHelper.elapsedRealtime(); 546 when(mTimeServiceHelper.elapsedRealtime()).thenReturn(currentElapsedRealtime + millis); 547 long currentTimeMillis = mTimeServiceHelper.currentTimeMillis(); 548 when(mTimeServiceHelper.currentTimeMillis()).thenReturn(currentTimeMillis + millis); 549 } 550 551 void nitzSignalReceived(TimeStampedValue<NitzData> nitzSignal) { 552 mNitzStateMachine.handleNitzReceived(nitzSignal); 553 } 554 555 void verifyTimeZoneWasNotSet() { 556 verify(mTimeServiceHelper, times(0)).setDeviceTimeZone(any(String.class)); 557 } 558 559 void verifyTimeZoneWasSet(String timeZoneId) { 560 verifyTimeZoneWasSet(timeZoneId, 1 /* times */); 561 } 562 563 void verifyTimeZoneWasSet(String timeZoneId, int times) { 564 verify(mTimeServiceHelper, times(times)).setDeviceTimeZone(timeZoneId); 565 } 566 567 void verifyTimeWasNotSet() { 568 verify(mTimeServiceHelper, times(0)).setDeviceTime(anyLong()); 569 } 570 571 void verifyTimeWasSet(long expectedTimeMillis) { 572 ArgumentCaptor<Long> timeServiceTimeCaptor = ArgumentCaptor.forClass(Long.TYPE); 573 verify(mTimeServiceHelper, times(1)).setDeviceTime(timeServiceTimeCaptor.capture()); 574 assertEquals(expectedTimeMillis, (long) timeServiceTimeCaptor.getValue()); 575 } 576 577 /** 578 * Used after calling verify... methods to reset expectations. 579 */ 580 void resetInvocations() { 581 clearInvocations(mTimeServiceHelper); 582 } 583 584 void checkNoUnverifiedSetOperations() { 585 NitzStateMachineTest.checkNoUnverifiedSetOperations(mTimeServiceHelper); 586 } 587 } 588 589 /** A class used to construct a Device. */ 590 class DeviceBuilder { 591 592 private long mInitialSystemClock; 593 private long mInitialRealtimeMillis; 594 private boolean mTimeDetectionEnabled; 595 private boolean mTimeZoneDetectionEnabled; 596 private boolean mTimeZoneSettingInitialized; 597 598 Device initialize() { 599 Device device = new Device(mInitialSystemClock, mInitialRealtimeMillis, 600 mTimeDetectionEnabled, mTimeZoneDetectionEnabled, mTimeZoneSettingInitialized); 601 device.initialize(); 602 return device; 603 } 604 605 DeviceBuilder setTimeDetectionEnabled(boolean enabled) { 606 mTimeDetectionEnabled = enabled; 607 return this; 608 } 609 610 DeviceBuilder setTimeZoneDetectionEnabled(boolean enabled) { 611 mTimeZoneDetectionEnabled = enabled; 612 return this; 613 } 614 615 DeviceBuilder setTimeZoneSettingInitialized(boolean initialized) { 616 mTimeZoneSettingInitialized = initialized; 617 return this; 618 } 619 620 DeviceBuilder setClocksFromScenario(Scenario scenario) { 621 mInitialRealtimeMillis = scenario.getInitialRealTimeMillis(); 622 mInitialSystemClock = scenario.getInitialSystemClockMillis(); 623 return this; 624 } 625 } 626 627 /** 628 * A scenario used during tests. Describes a fictional reality. 629 */ 630 static class Scenario { 631 632 private final long mInitialDeviceSystemClockMillis; 633 private final long mInitialDeviceRealtimeMillis; 634 private final long mActualTimeMillis; 635 private final TimeZone mZone; 636 private final String mNetworkCountryIsoCode; 637 638 private TimeStampedValue<NitzData> mNitzSignal; 639 640 Scenario(long initialDeviceSystemClock, long elapsedRealtime, long timeMillis, 641 String zoneId, String countryIsoCode) { 642 mInitialDeviceSystemClockMillis = initialDeviceSystemClock; 643 mActualTimeMillis = timeMillis; 644 mInitialDeviceRealtimeMillis = elapsedRealtime; 645 mZone = TimeZone.getTimeZone(zoneId); 646 mNetworkCountryIsoCode = countryIsoCode; 647 } 648 649 TimeStampedValue<NitzData> getNitzSignal() { 650 if (mNitzSignal == null) { 651 int[] offsets = new int[2]; 652 mZone.getOffset(mActualTimeMillis, false /* local */, offsets); 653 int zoneOffsetMillis = offsets[0] + offsets[1]; 654 NitzData nitzData = NitzData 655 .createForTests(zoneOffsetMillis, offsets[1], mActualTimeMillis, null); 656 mNitzSignal = new TimeStampedValue<>(nitzData, mInitialDeviceRealtimeMillis); 657 } 658 return mNitzSignal; 659 } 660 661 long getInitialRealTimeMillis() { 662 return mInitialDeviceRealtimeMillis; 663 } 664 665 long getInitialSystemClockMillis() { 666 return mInitialDeviceSystemClockMillis; 667 } 668 669 String getNetworkCountryIsoCode() { 670 return mNetworkCountryIsoCode; 671 } 672 673 String getTimeZoneId() { 674 return mZone.getID(); 675 } 676 677 long getActualTimeMillis() { 678 return mActualTimeMillis; 679 } 680 681 static class Builder { 682 683 private long mInitialDeviceSystemClockMillis; 684 private long mInitialDeviceRealtimeMillis; 685 private long mActualTimeMillis; 686 private String mZoneId; 687 private String mCountryIsoCode; 688 689 Builder setInitialDeviceSystemClockUtc(int year, int monthInYear, int day, 690 int hourOfDay, int minute, int second) { 691 mInitialDeviceSystemClockMillis = createUtcTime(year, monthInYear, day, hourOfDay, 692 minute, second); 693 return this; 694 } 695 696 Builder setInitialDeviceRealtimeMillis(long realtimeMillis) { 697 mInitialDeviceRealtimeMillis = realtimeMillis; 698 return this; 699 } 700 701 Builder setActualTimeUtc(int year, int monthInYear, int day, int hourOfDay, 702 int minute, int second) { 703 mActualTimeMillis = createUtcTime(year, monthInYear, day, hourOfDay, minute, 704 second); 705 return this; 706 } 707 708 Builder setTimeZone(String zoneId) { 709 mZoneId = zoneId; 710 return this; 711 } 712 713 Builder setCountryIso(String isoCode) { 714 mCountryIsoCode = isoCode; 715 return this; 716 } 717 718 Scenario build() { 719 return new Scenario(mInitialDeviceSystemClockMillis, mInitialDeviceRealtimeMillis, 720 mActualTimeMillis, mZoneId, mCountryIsoCode); 721 } 722 } 723 } 724 725 /** 726 * Confirms all mTimeServiceHelper side effects were verified. 727 */ 728 private static void checkNoUnverifiedSetOperations(TimeServiceHelper mTimeServiceHelper) { 729 // We don't care about current auto time / time zone state retrievals / listening so we can 730 // use "at least 0" times to indicate they don't matter. 731 verify(mTimeServiceHelper, atLeast(0)).setListener(any()); 732 verify(mTimeServiceHelper, atLeast(0)).isTimeDetectionEnabled(); 733 verify(mTimeServiceHelper, atLeast(0)).isTimeZoneDetectionEnabled(); 734 verify(mTimeServiceHelper, atLeast(0)).isTimeZoneSettingInitialized(); 735 verify(mTimeServiceHelper, atLeast(0)).elapsedRealtime(); 736 verify(mTimeServiceHelper, atLeast(0)).currentTimeMillis(); 737 verifyNoMoreInteractions(mTimeServiceHelper); 738 } 739 } 740