1 /* 2 * Copyright (C) 2020 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 android.time.cts.host; 18 19 20 import static android.app.time.cts.shell.DeviceConfigKeys.LocationTimeZoneManager.KEY_LTZP_EVENT_FILTERING_AGE_THRESHOLD_MILLIS; 21 import static android.app.time.cts.shell.DeviceConfigKeys.NAMESPACE_SYSTEM_TIME; 22 import static android.app.time.cts.shell.DeviceConfigShellHelper.SYNC_DISABLED_MODE_UNTIL_REBOOT; 23 import static android.app.time.cts.shell.FakeTimeZoneProviderAppShellHelper.DEPENDENCY_STATUS_OK; 24 import static android.app.time.cts.shell.FakeTimeZoneProviderAppShellHelper.DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE; 25 import static android.app.time.cts.shell.FakeTimeZoneProviderAppShellHelper.FAKE_TZPS_APP_APK; 26 import static android.app.time.cts.shell.FakeTimeZoneProviderAppShellHelper.FAKE_TZPS_APP_PACKAGE; 27 import static android.app.time.cts.shell.FakeTimeZoneProviderAppShellHelper.OPERATION_STATUS_OK; 28 import static android.app.time.cts.shell.FakeTimeZoneProviderAppShellHelper.PROVIDER_STATE_CERTAIN; 29 import static android.app.time.cts.shell.FakeTimeZoneProviderAppShellHelper.PROVIDER_STATE_DISABLED; 30 import static android.app.time.cts.shell.FakeTimeZoneProviderAppShellHelper.PROVIDER_STATE_INITIALIZING; 31 import static android.app.time.cts.shell.FakeTimeZoneProviderAppShellHelper.PROVIDER_STATE_UNCERTAIN; 32 33 import static org.junit.Assert.assertEquals; 34 import static org.junit.Assert.assertFalse; 35 import static org.junit.Assert.assertTrue; 36 37 import android.app.time.ControllerStateEnum; 38 import android.app.time.DetectionAlgorithmStatusEnum; 39 import android.app.time.LocationTimeZoneManagerServiceStateProto; 40 import android.app.time.LocationTimeZoneProviderEventProto; 41 import android.app.time.TimeZoneProviderStateEnum; 42 import android.app.time.TimeZoneProviderStateProto; 43 import android.app.time.cts.shell.DeviceConfigShellHelper; 44 import android.app.time.cts.shell.DeviceShellCommandExecutor; 45 import android.app.time.cts.shell.FakeTimeZoneProviderAppShellHelper; 46 import android.app.time.cts.shell.FakeTimeZoneProviderAppShellHelper.FakeTimeZoneProviderShellHelper; 47 import android.app.time.cts.shell.LocationShellHelper; 48 import android.app.time.cts.shell.LocationTimeZoneManagerShellHelper; 49 import android.app.time.cts.shell.TimeZoneDetectorShellHelper; 50 import android.app.time.cts.shell.host.HostShellCommandExecutor; 51 52 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 53 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 54 55 import com.google.protobuf.Parser; 56 57 import org.junit.After; 58 import org.junit.Before; 59 import org.junit.Test; 60 import org.junit.runner.RunWith; 61 62 import java.time.Duration; 63 import java.util.Arrays; 64 import java.util.List; 65 66 /** 67 * Host-side CTS tests for the location time zone manager service. There are plenty of unit tests 68 * for individual components but manufacturers don't have to run them. This test is intended to 69 * provide confidence that the specific device configuration is likely to work as it should, i.e. 70 * this tests the actual location_time_zone_manager service on a given device. 71 * 72 * <p>Because there are a large set of possibilities, this test has to handle them all: 73 * <ul> 74 * <li>location_time_zone_manager service disabled</li> 75 * <li>location_time_zone_manager service enabled, but no LTZPs configured</li> 76 * <li>location_time_zone_manager service enabled, with LTZPs configured</li> 77 * </ul> 78 * 79 * <p>Further complicating things is that devices may or may not have telephony detection, which 80 * influences what settings are used. 81 */ 82 @RunWith(DeviceJUnit4ClassRunner.class) 83 public class LocationTimeZoneManagerHostTest extends BaseHostJUnit4Test { 84 85 private static final String NON_EXISTENT_TZPS_APP_PACKAGE = "foobar"; 86 87 private boolean mIsTelephonyDetectionSupported; 88 private boolean mOriginalLocationEnabled; 89 private boolean mOriginalAutoDetectionEnabled; 90 private boolean mOriginalGeoDetectionEnabled; 91 private TimeZoneDetectorShellHelper mTimeZoneDetectorShellHelper; 92 private LocationTimeZoneManagerShellHelper mLocationTimeZoneManagerShellHelper; 93 private DeviceConfigShellHelper mDeviceConfigShellHelper; 94 private DeviceConfigShellHelper.PreTestState mDeviceConfigPreTestState; 95 private LocationShellHelper mLocationShellHelper; 96 private FakeTimeZoneProviderShellHelper mPrimaryFakeTimeZoneProviderShellHelper; 97 private FakeTimeZoneProviderShellHelper mSecondaryFakeTimeZoneProviderShellHelper; 98 99 @Before setUp()100 public void setUp() throws Exception { 101 DeviceShellCommandExecutor shellCommandExecutor = new HostShellCommandExecutor(getDevice()); 102 mLocationTimeZoneManagerShellHelper = 103 new LocationTimeZoneManagerShellHelper(shellCommandExecutor); 104 105 // Confirm the service being tested is present. It can be turned off permanently in config, 106 // in which case there's nothing about it to test. 107 mLocationTimeZoneManagerShellHelper.assumeLocationTimeZoneManagerIsPresent(); 108 109 // Install the app that hosts the fake providers. 110 // Installations are tracked in BaseHostJUnit4Test and uninstalled automatically. 111 installPackage(FAKE_TZPS_APP_APK); 112 113 mTimeZoneDetectorShellHelper = new TimeZoneDetectorShellHelper(shellCommandExecutor); 114 mLocationShellHelper = new LocationShellHelper(shellCommandExecutor); 115 mDeviceConfigShellHelper = new DeviceConfigShellHelper(shellCommandExecutor); 116 117 // Stop device_config updates for the duration of the test. 118 mDeviceConfigPreTestState = mDeviceConfigShellHelper.setSyncModeForTest( 119 SYNC_DISABLED_MODE_UNTIL_REBOOT, NAMESPACE_SYSTEM_TIME); 120 121 // The isGeoDetectionEnabled() setting is only used when both time zone detection algorithms 122 // are supported on a device. It allows the user to select between them. When only 123 // location-based detection is supported then the setting is not used, i.e. the device 124 // behaves like it is hardcode to ON. Attempts to change the geo detection enabled setting 125 // when it isn't used by a device will fail so must be avoided. 126 // Location detection must be supported to reach this point. Capture the state of telephony 127 // algorithm support for later checks. 128 mIsTelephonyDetectionSupported = 129 mTimeZoneDetectorShellHelper.isTelephonyDetectionSupported(); 130 131 // These original values try to record the raw value of the settings before the test ran: 132 // they may be ignored by the location_time_zone_manager service when they have no meaning. 133 // Unfortunately, we cannot tell if the value returned is the result of setting defaults or 134 // real values, which means we may not return things exactly as they were. To do better 135 // would require looking at raw settings values and use internal knowledge of settings keys. 136 mOriginalAutoDetectionEnabled = mTimeZoneDetectorShellHelper.isAutoDetectionEnabled(); 137 mOriginalGeoDetectionEnabled = mTimeZoneDetectorShellHelper.isGeoDetectionEnabled(); 138 139 mLocationTimeZoneManagerShellHelper.stop(); 140 141 // Make sure location is enabled, otherwise the geo detection feature cannot operate. 142 mOriginalLocationEnabled = mLocationShellHelper.isLocationEnabledForCurrentUser(); 143 if (!mOriginalLocationEnabled) { 144 mLocationShellHelper.setLocationEnabledForCurrentUser(true); 145 } 146 147 // Restart the location_time_zone_manager with a do-nothing test config; some settings 148 // values cannot be set when the service knows that the settings won't be used. Devices 149 // can be encountered with the location_time_zone_manager enabled but with no providers 150 // installed. Starting the service with a valid-looking test provider config means we know 151 // settings changes will be accepted regardless of the real config. 152 String testPrimaryLocationTimeZoneProviderPackageName = NON_EXISTENT_TZPS_APP_PACKAGE; 153 String testSecondaryLocationTimeZoneProviderPackageName = null; 154 mLocationTimeZoneManagerShellHelper.startWithTestProviders( 155 testPrimaryLocationTimeZoneProviderPackageName, 156 testSecondaryLocationTimeZoneProviderPackageName, 157 false /* recordProviderStates */); 158 159 // Begin all tests with auto detection turned off. 160 if (mOriginalAutoDetectionEnabled) { 161 mTimeZoneDetectorShellHelper.setAutoDetectionEnabled(false); 162 } 163 164 if (mIsTelephonyDetectionSupported) { 165 // When the telephony detection algorithm is also supported on the device, we need to 166 // set the device settings so that location detection will be used. This behavior is 167 // implicit when the device only supports the location detection algorithm. 168 if (!mOriginalGeoDetectionEnabled) { 169 mTimeZoneDetectorShellHelper.setGeoDetectionEnabled(true); 170 } 171 } 172 173 // All tests begin with the location_time_zone_manager stopped so that fake providers can be 174 // configured. 175 mLocationTimeZoneManagerShellHelper.stop(); 176 177 // Make sure the fake provider APK install started above has completed before tests try to 178 // use the fake providers. 179 FakeTimeZoneProviderAppShellHelper fakeTimeZoneProviderAppShellHelper = 180 new FakeTimeZoneProviderAppShellHelper(shellCommandExecutor); 181 // Delay until the fake TZPS app can be found. 182 fakeTimeZoneProviderAppShellHelper.waitForInstallation(); 183 mPrimaryFakeTimeZoneProviderShellHelper = 184 fakeTimeZoneProviderAppShellHelper.getPrimaryLocationProviderHelper(); 185 mSecondaryFakeTimeZoneProviderShellHelper = 186 fakeTimeZoneProviderAppShellHelper.getSecondaryLocationProviderHelper(); 187 } 188 189 @After tearDown()190 public void tearDown() throws Exception { 191 if (!mLocationTimeZoneManagerShellHelper.isLocationTimeZoneManagerPresent()) { 192 // Nothing to tear down. 193 return; 194 } 195 196 // Restart the location_time_zone_manager with a test config so that the device can be set 197 // back to the starting state regardless of how the test left things. 198 mLocationTimeZoneManagerShellHelper.stop(); 199 String testPrimaryLocationTimeZoneProviderPackageName = NON_EXISTENT_TZPS_APP_PACKAGE; 200 String testSecondaryLocationTimeZoneProviderPackageName = null; 201 mLocationTimeZoneManagerShellHelper.startWithTestProviders( 202 testPrimaryLocationTimeZoneProviderPackageName, 203 testSecondaryLocationTimeZoneProviderPackageName, 204 false /* recordProviderStates */); 205 206 if (mIsTelephonyDetectionSupported) { 207 if (mTimeZoneDetectorShellHelper.isGeoDetectionEnabled() 208 != mOriginalGeoDetectionEnabled) { 209 mTimeZoneDetectorShellHelper.setGeoDetectionEnabled(mOriginalGeoDetectionEnabled); 210 } 211 } 212 213 if (mTimeZoneDetectorShellHelper.isAutoDetectionEnabled() 214 != mOriginalAutoDetectionEnabled) { 215 mTimeZoneDetectorShellHelper.setAutoDetectionEnabled(mOriginalAutoDetectionEnabled); 216 } 217 218 // Everything else can be reset without worrying about the providers. 219 mLocationTimeZoneManagerShellHelper.stop(); 220 221 mLocationShellHelper.setLocationEnabledForCurrentUser(mOriginalLocationEnabled); 222 mDeviceConfigShellHelper.restoreDeviceConfigStateForTest(mDeviceConfigPreTestState); 223 224 // Attempt to start the service without test providers. It may not start if there are no 225 // providers configured, but that is ok. 226 mLocationTimeZoneManagerShellHelper.start(); 227 } 228 229 @Test testOnlyPrimary_suggestionMade()230 public void testOnlyPrimary_suggestionMade() throws Exception { 231 testOnlyPrimary_suggestionMade(false); 232 } 233 234 @Test testOnlyPrimary_suggestionMade_legacy()235 public void testOnlyPrimary_suggestionMade_legacy() throws Exception { 236 testOnlyPrimary_suggestionMade(true); 237 } 238 239 /** Tests what happens when there's only a primary provider and it makes a suggestion. */ testOnlyPrimary_suggestionMade(boolean useLegacyApi)240 private void testOnlyPrimary_suggestionMade(boolean useLegacyApi) throws Exception { 241 String testPrimaryLocationTimeZoneProviderPackageName = FAKE_TZPS_APP_PACKAGE; 242 String testSecondaryLocationTimeZoneProviderPackageName = null; 243 mLocationTimeZoneManagerShellHelper.startWithTestProviders( 244 testPrimaryLocationTimeZoneProviderPackageName, 245 testSecondaryLocationTimeZoneProviderPackageName, 246 true /* recordProviderStates */); 247 248 // Turn on auto detection, which should activate the location time zone algorithm. 249 mTimeZoneDetectorShellHelper.setAutoDetectionEnabled(true); 250 251 mPrimaryFakeTimeZoneProviderShellHelper.assertCreated(); 252 mSecondaryFakeTimeZoneProviderShellHelper.assertNotCreated(); 253 254 { 255 LocationTimeZoneManagerServiceStateProto serviceState = dumpServiceState(); 256 assertControllerStateHistory(serviceState, 257 ControllerStateEnum.CONTROLLER_STATE_PROVIDERS_INITIALIZING, 258 ControllerStateEnum.CONTROLLER_STATE_STOPPED, 259 ControllerStateEnum.CONTROLLER_STATE_INITIALIZING); 260 assertProviderStates(serviceState.getPrimaryProviderStatesList(), 261 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_DISABLED, 262 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_INITIALIZING); 263 mPrimaryFakeTimeZoneProviderShellHelper.assertCurrentState(PROVIDER_STATE_INITIALIZING); 264 265 assertProviderStates(serviceState.getSecondaryProviderStatesList(), 266 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_DISABLED, 267 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_PERM_FAILED); 268 } 269 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 270 271 reportSuccess(mPrimaryFakeTimeZoneProviderShellHelper, "Europe/London", useLegacyApi); 272 273 { 274 LocationTimeZoneManagerServiceStateProto serviceState = dumpServiceState(); 275 assertControllerStateHistory(serviceState, 276 ControllerStateEnum.CONTROLLER_STATE_CERTAIN); 277 assertLastEventWithSuggestion(serviceState, "Europe/London"); 278 assertProviderStates(serviceState.getPrimaryProviderStatesList(), 279 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_CERTAIN); 280 mPrimaryFakeTimeZoneProviderShellHelper.assertCurrentState(PROVIDER_STATE_CERTAIN); 281 282 assertProviderStates(serviceState.getSecondaryProviderStatesList()); 283 } 284 } 285 286 @Test test_dupeSuggestionsMade_rateLimited()287 public void test_dupeSuggestionsMade_rateLimited() throws Exception { 288 test_dupeSuggestionsMade_rateLimited(false); 289 } 290 291 @Test test_dupeSuggestionsMade_rateLimited_legacy()292 public void test_dupeSuggestionsMade_rateLimited_legacy() throws Exception { 293 test_dupeSuggestionsMade_rateLimited(true); 294 } 295 296 /** 297 * Demonstrates that duplicate equivalent reports made by location time zone providers within 298 * a threshold time are ignored. It focuses on a single LTZP setup (primary only); the behavior 299 * for the secondary is assumed to be identical. 300 */ test_dupeSuggestionsMade_rateLimited(boolean useLegacyApis)301 private void test_dupeSuggestionsMade_rateLimited(boolean useLegacyApis) throws Exception { 302 // Set the rate setting sufficiently high that rate limiting will definitely take place. 303 mDeviceConfigShellHelper.put(NAMESPACE_SYSTEM_TIME, 304 KEY_LTZP_EVENT_FILTERING_AGE_THRESHOLD_MILLIS, 305 Long.toString(Duration.ofMinutes(10).toMillis())); 306 307 String testPrimaryLocationTimeZoneProviderPackageName = FAKE_TZPS_APP_PACKAGE; 308 String testSecondaryLocationTimeZoneProviderPackageName = null; 309 mLocationTimeZoneManagerShellHelper.startWithTestProviders( 310 testPrimaryLocationTimeZoneProviderPackageName, 311 testSecondaryLocationTimeZoneProviderPackageName, 312 true /* recordProviderStates */); 313 // Turn on auto detection, which should activate the location time zone algorithm. 314 mTimeZoneDetectorShellHelper.setAutoDetectionEnabled(true); 315 316 mPrimaryFakeTimeZoneProviderShellHelper.assertCreated(); 317 mSecondaryFakeTimeZoneProviderShellHelper.assertNotCreated(); 318 319 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 320 321 // Report a new time zone. 322 reportSuccess(mPrimaryFakeTimeZoneProviderShellHelper, "Europe/London", useLegacyApis); 323 assertPrimaryReportedCertain(); 324 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 325 326 // Duplicate time zone suggestion. 327 reportSuccess(mPrimaryFakeTimeZoneProviderShellHelper, "Europe/London", useLegacyApis); 328 assertPrimaryMadeNoReport(); 329 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 330 331 // Report a new time zone. 332 reportSuccess(mPrimaryFakeTimeZoneProviderShellHelper, "Europe/Paris", useLegacyApis); 333 assertPrimaryReportedCertain(); 334 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 335 336 // Duplicate time zone suggestion. 337 reportSuccess(mPrimaryFakeTimeZoneProviderShellHelper, "Europe/Paris", useLegacyApis); 338 assertPrimaryMadeNoReport(); 339 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 340 341 // Report uncertain. 342 reportUncertain(mPrimaryFakeTimeZoneProviderShellHelper, useLegacyApis); 343 assertPrimaryReportedUncertain(); 344 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 345 346 // Duplicate uncertain report. 347 reportUncertain(mPrimaryFakeTimeZoneProviderShellHelper, useLegacyApis); 348 assertPrimaryMadeNoReport(); 349 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 350 351 // Report a new time zone. 352 reportSuccess(mPrimaryFakeTimeZoneProviderShellHelper, "Europe/Paris", useLegacyApis); 353 assertPrimaryReportedCertain(); 354 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 355 } 356 357 @Test test_dupeSuggestionsMade_notRateLimited()358 public void test_dupeSuggestionsMade_notRateLimited() throws Exception { 359 test_dupeSuggestionsMade_notRateLimited(false); 360 } 361 362 @Test test_dupeSuggestionsMade_notRateLimited_legacy()363 public void test_dupeSuggestionsMade_notRateLimited_legacy() throws Exception { 364 test_dupeSuggestionsMade_notRateLimited(true); 365 } 366 367 /** 368 * Demonstrates that duplicate equivalent reports made by location time zone providers above 369 * a threshold time are not filtered. It focuses on a single LTZP setup (primary only); the 370 * behavior for the secondary is assumed to be identical. 371 */ test_dupeSuggestionsMade_notRateLimited(boolean useLegacyApis)372 private void test_dupeSuggestionsMade_notRateLimited(boolean useLegacyApis) throws Exception { 373 // Set the rate sufficiently low that rate limiting will not take place. 374 mDeviceConfigShellHelper.put(NAMESPACE_SYSTEM_TIME, 375 KEY_LTZP_EVENT_FILTERING_AGE_THRESHOLD_MILLIS, 376 "0"); 377 378 String testPrimaryLocationTimeZoneProviderPackageName = FAKE_TZPS_APP_PACKAGE; 379 String testSecondaryLocationTimeZoneProviderPackageName = null; 380 mLocationTimeZoneManagerShellHelper.startWithTestProviders( 381 testPrimaryLocationTimeZoneProviderPackageName, 382 testSecondaryLocationTimeZoneProviderPackageName, 383 true /* recordProviderStates */); 384 // Turn on auto detection, which should activate the location time zone algorithm. 385 mTimeZoneDetectorShellHelper.setAutoDetectionEnabled(true); 386 mPrimaryFakeTimeZoneProviderShellHelper.assertCreated(); 387 mSecondaryFakeTimeZoneProviderShellHelper.assertNotCreated(); 388 389 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 390 391 // Report a new time zone. 392 reportSuccess(mPrimaryFakeTimeZoneProviderShellHelper, "Europe/London", useLegacyApis); 393 assertPrimaryReportedCertain(); 394 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 395 396 // Duplicate time zone suggestion. 397 reportSuccess(mPrimaryFakeTimeZoneProviderShellHelper, "Europe/London", useLegacyApis); 398 assertPrimaryReportedCertain(); 399 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 400 401 // Report uncertain. 402 reportUncertain(mPrimaryFakeTimeZoneProviderShellHelper, useLegacyApis); 403 assertPrimaryReportedUncertain(); 404 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 405 406 // Duplicate uncertain report. 407 reportUncertain(mPrimaryFakeTimeZoneProviderShellHelper, useLegacyApis); 408 assertPrimaryReportedUncertain(); 409 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 410 } 411 assertPrimaryReportedCertain()412 private void assertPrimaryReportedCertain() throws Exception { 413 LocationTimeZoneManagerServiceStateProto serviceState = dumpServiceState(); 414 assertProviderStates(serviceState.getPrimaryProviderStatesList(), 415 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_CERTAIN); 416 } 417 assertPrimaryMadeNoReport()418 private void assertPrimaryMadeNoReport() throws Exception { 419 LocationTimeZoneManagerServiceStateProto serviceState = dumpServiceState(); 420 assertProviderStates(serviceState.getPrimaryProviderStatesList()); 421 } 422 assertPrimaryReportedUncertain()423 private void assertPrimaryReportedUncertain() throws Exception { 424 LocationTimeZoneManagerServiceStateProto serviceState = dumpServiceState(); 425 assertProviderStates(serviceState.getPrimaryProviderStatesList(), 426 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_UNCERTAIN); 427 } 428 429 /** Tests what happens when there's only a secondary provider and it makes a suggestion. */ 430 @Test testOnlySecondary_suggestionMade()431 public void testOnlySecondary_suggestionMade() throws Exception { 432 testOnlySecondary_suggestionMade(false); 433 } 434 435 /** Tests what happens when there's only a secondary provider and it makes a suggestion. */ 436 @Test testOnlySecondary_suggestionMade_legacy()437 public void testOnlySecondary_suggestionMade_legacy() throws Exception { 438 testOnlySecondary_suggestionMade(true); 439 } 440 testOnlySecondary_suggestionMade(boolean useLegacyApis)441 private void testOnlySecondary_suggestionMade(boolean useLegacyApis) throws Exception { 442 String testPrimaryLocationTimeZoneProviderPackageName = null; 443 String testSecondaryLocationTimeZoneProviderPackageName = FAKE_TZPS_APP_PACKAGE; 444 mLocationTimeZoneManagerShellHelper.startWithTestProviders( 445 testPrimaryLocationTimeZoneProviderPackageName, 446 testSecondaryLocationTimeZoneProviderPackageName, 447 true /* recordProviderStates */); 448 // Turn on auto detection, which should activate the location time zone algorithm. 449 mTimeZoneDetectorShellHelper.setAutoDetectionEnabled(true); 450 mPrimaryFakeTimeZoneProviderShellHelper.assertNotCreated(); 451 mSecondaryFakeTimeZoneProviderShellHelper.assertCreated(); 452 453 { 454 LocationTimeZoneManagerServiceStateProto serviceState = dumpServiceState(); 455 assertControllerStateHistory(serviceState, 456 ControllerStateEnum.CONTROLLER_STATE_PROVIDERS_INITIALIZING, 457 ControllerStateEnum.CONTROLLER_STATE_STOPPED, 458 ControllerStateEnum.CONTROLLER_STATE_INITIALIZING); 459 assertLastEventWithoutSuggestion(serviceState); 460 assertProviderStates(serviceState.getPrimaryProviderStatesList(), 461 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_DISABLED, 462 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_PERM_FAILED); 463 464 assertProviderStates(serviceState.getSecondaryProviderStatesList(), 465 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_DISABLED, 466 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_INITIALIZING); 467 } 468 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 469 470 reportSuccess(mSecondaryFakeTimeZoneProviderShellHelper, "Europe/London", useLegacyApis); 471 472 { 473 LocationTimeZoneManagerServiceStateProto serviceState = dumpServiceState(); 474 assertControllerStateHistory(serviceState, 475 ControllerStateEnum.CONTROLLER_STATE_CERTAIN); 476 assertLastEventWithSuggestion(serviceState, "Europe/London"); 477 assertProviderStates(serviceState.getPrimaryProviderStatesList()); 478 479 assertProviderStates(serviceState.getSecondaryProviderStatesList(), 480 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_CERTAIN); 481 mSecondaryFakeTimeZoneProviderShellHelper.assertCurrentState(PROVIDER_STATE_CERTAIN); 482 } 483 } 484 485 @Test testPrimaryAndSecondary()486 public void testPrimaryAndSecondary() throws Exception { 487 testPrimaryAndSecondary(false); 488 } 489 490 @Test testPrimaryAndSecondary_legacy()491 public void testPrimaryAndSecondary_legacy() throws Exception { 492 testPrimaryAndSecondary(true); 493 } 494 495 /** 496 * Tests what happens when there's both a primary and a secondary provider, the primary starts 497 * by being uncertain, the secondary makes a suggestion, then the primary makes a suggestion. 498 */ testPrimaryAndSecondary(boolean useLegacyApis)499 private void testPrimaryAndSecondary(boolean useLegacyApis) throws Exception { 500 String testPrimaryLocationTimeZoneProviderPackageName = FAKE_TZPS_APP_PACKAGE; 501 String testSecondaryLocationTimeZoneProviderPackageName = FAKE_TZPS_APP_PACKAGE; 502 mLocationTimeZoneManagerShellHelper.startWithTestProviders( 503 testPrimaryLocationTimeZoneProviderPackageName, 504 testSecondaryLocationTimeZoneProviderPackageName, 505 true /* recordProviderStates*/); 506 // Turn on auto detection, which should activate the location time zone algorithm. 507 mTimeZoneDetectorShellHelper.setAutoDetectionEnabled(true); 508 mPrimaryFakeTimeZoneProviderShellHelper.assertCreated(); 509 mSecondaryFakeTimeZoneProviderShellHelper.assertCreated(); 510 511 { 512 LocationTimeZoneManagerServiceStateProto serviceState = dumpServiceState(); 513 assertControllerStateHistory(serviceState, 514 ControllerStateEnum.CONTROLLER_STATE_PROVIDERS_INITIALIZING, 515 ControllerStateEnum.CONTROLLER_STATE_STOPPED, 516 ControllerStateEnum.CONTROLLER_STATE_INITIALIZING); 517 assertLastEventWithoutSuggestion(serviceState); 518 assertProviderStates(serviceState.getPrimaryProviderStatesList(), 519 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_DISABLED, 520 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_INITIALIZING); 521 mPrimaryFakeTimeZoneProviderShellHelper.assertCurrentState(PROVIDER_STATE_INITIALIZING); 522 523 assertProviderStates(serviceState.getSecondaryProviderStatesList(), 524 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_DISABLED); 525 mSecondaryFakeTimeZoneProviderShellHelper.assertCurrentState(PROVIDER_STATE_DISABLED); 526 } 527 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 528 529 // Make the primary report being uncertain. This should cause the secondary to be started. 530 reportUncertain(mPrimaryFakeTimeZoneProviderShellHelper, useLegacyApis); 531 532 { 533 LocationTimeZoneManagerServiceStateProto serviceState = dumpServiceState(); 534 assertControllerStateHistory(serviceState); 535 assertLastEventWithoutSuggestion(serviceState); 536 assertProviderStates(serviceState.getPrimaryProviderStatesList(), 537 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_UNCERTAIN); 538 mPrimaryFakeTimeZoneProviderShellHelper.assertCurrentState(PROVIDER_STATE_UNCERTAIN); 539 540 assertProviderStates(serviceState.getSecondaryProviderStatesList(), 541 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_INITIALIZING); 542 mSecondaryFakeTimeZoneProviderShellHelper.assertCurrentState( 543 PROVIDER_STATE_INITIALIZING); 544 } 545 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 546 547 // Make the secondary report being certain. 548 reportSuccess(mSecondaryFakeTimeZoneProviderShellHelper, "Europe/London", useLegacyApis); 549 550 { 551 LocationTimeZoneManagerServiceStateProto serviceState = dumpServiceState(); 552 assertControllerStateHistory(serviceState, 553 ControllerStateEnum.CONTROLLER_STATE_CERTAIN); 554 assertLastEventWithSuggestion(serviceState, "Europe/London"); 555 assertProviderStates(serviceState.getPrimaryProviderStatesList()); 556 mPrimaryFakeTimeZoneProviderShellHelper.assertCurrentState(PROVIDER_STATE_UNCERTAIN); 557 558 assertProviderStates(serviceState.getSecondaryProviderStatesList(), 559 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_CERTAIN); 560 mSecondaryFakeTimeZoneProviderShellHelper.assertCurrentState(PROVIDER_STATE_CERTAIN); 561 } 562 mLocationTimeZoneManagerShellHelper.clearRecordedProviderStates(); 563 564 // Make the primary report being certain. 565 reportSuccess(mPrimaryFakeTimeZoneProviderShellHelper, "Europe/Paris", useLegacyApis); 566 567 { 568 LocationTimeZoneManagerServiceStateProto serviceState = dumpServiceState(); 569 assertControllerStateHistory(serviceState); 570 assertLastEventWithSuggestion(serviceState, "Europe/Paris"); 571 assertProviderStates(serviceState.getPrimaryProviderStatesList(), 572 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_CERTAIN); 573 mPrimaryFakeTimeZoneProviderShellHelper.assertCurrentState(PROVIDER_STATE_CERTAIN); 574 575 assertProviderStates(serviceState.getSecondaryProviderStatesList(), 576 TimeZoneProviderStateEnum.TIME_ZONE_PROVIDER_STATE_DISABLED); 577 mSecondaryFakeTimeZoneProviderShellHelper.assertCurrentState(PROVIDER_STATE_DISABLED); 578 } 579 } 580 assertControllerStateHistory( LocationTimeZoneManagerServiceStateProto serviceState, ControllerStateEnum... expectedStates)581 private static void assertControllerStateHistory( 582 LocationTimeZoneManagerServiceStateProto serviceState, 583 ControllerStateEnum... expectedStates) { 584 List<ControllerStateEnum> expectedStatesList = Arrays.asList(expectedStates); 585 List<ControllerStateEnum> actualStates = serviceState.getControllerStatesList(); 586 assertEquals(expectedStatesList, actualStates); 587 } 588 assertLastEventWithoutSuggestion( LocationTimeZoneManagerServiceStateProto actualServiceState)589 private static void assertLastEventWithoutSuggestion( 590 LocationTimeZoneManagerServiceStateProto actualServiceState) { 591 assertTrue(actualServiceState.hasLastEvent()); 592 assertFalse(actualServiceState.getLastEvent().hasSuggestion()); 593 594 LocationTimeZoneProviderEventProto lastEvent = actualServiceState.getLastEvent(); 595 assertEquals(DetectionAlgorithmStatusEnum.DETECTION_ALGORITHM_STATUS_RUNNING, 596 lastEvent.getAlgorithmStatus().getStatus()); 597 } 598 assertLastEventWithSuggestion( LocationTimeZoneManagerServiceStateProto actualServiceState, String... expectedTimeZones)599 private static void assertLastEventWithSuggestion( 600 LocationTimeZoneManagerServiceStateProto actualServiceState, 601 String... expectedTimeZones) { 602 assertFalse(expectedTimeZones == null || expectedTimeZones.length == 0); 603 604 assertTrue(actualServiceState.hasLastEvent()); 605 LocationTimeZoneProviderEventProto lastEvent = actualServiceState.getLastEvent(); 606 607 assertEquals(DetectionAlgorithmStatusEnum.DETECTION_ALGORITHM_STATUS_RUNNING, 608 lastEvent.getAlgorithmStatus().getStatus()); 609 610 List<String> expectedTimeZonesList = Arrays.asList(expectedTimeZones); 611 List<String> actualTimeZonesList = lastEvent.getSuggestion().getZoneIdsList(); 612 assertEquals(expectedTimeZonesList, actualTimeZonesList); 613 } 614 assertProviderStates(List<TimeZoneProviderStateProto> actualStates, TimeZoneProviderStateEnum... expectedStates)615 private static void assertProviderStates(List<TimeZoneProviderStateProto> actualStates, 616 TimeZoneProviderStateEnum... expectedStates) { 617 List<TimeZoneProviderStateEnum> expectedStatesList = Arrays.asList(expectedStates); 618 assertEquals("Expected states: " + expectedStatesList + ", but was " + actualStates, 619 expectedStatesList.size(), actualStates.size()); 620 for (int i = 0; i < expectedStatesList.size(); i++) { 621 assertEquals("Expected states: " + expectedStatesList + ", but was " + actualStates, 622 expectedStates[i], actualStates.get(i).getState()); 623 } 624 } 625 dumpServiceState()626 private LocationTimeZoneManagerServiceStateProto dumpServiceState() throws Exception { 627 byte[] protoBytes = mLocationTimeZoneManagerShellHelper.dumpState(); 628 Parser<LocationTimeZoneManagerServiceStateProto> parser = 629 LocationTimeZoneManagerServiceStateProto.parser(); 630 return parser.parseFrom(protoBytes); 631 } 632 633 /** 634 * Method used to report success when it shouldn't matter whether newer APIs that include status 635 * or older APIs that don't are used. The status provided for newer APIs is a generic "success" 636 * status. 637 */ reportSuccess(FakeTimeZoneProviderShellHelper providerShellHelper, String zoneId, boolean useLegacyApi)638 private void reportSuccess(FakeTimeZoneProviderShellHelper providerShellHelper, String zoneId, 639 boolean useLegacyApi) throws Exception { 640 if (useLegacyApi) { 641 providerShellHelper.reportSuccessLegacy(zoneId); 642 } else { 643 providerShellHelper.reportSuccess( 644 zoneId, DEPENDENCY_STATUS_OK, DEPENDENCY_STATUS_OK); 645 } 646 } 647 648 /** 649 * Method used to report uncertainty when it shouldn't matter whether newer APIs that include 650 * status or older APIs that don't are used. The status provided for newer APIs is a generic 651 * "uncertain" status that doesn't trigger any interesting behavior. 652 */ reportUncertain(FakeTimeZoneProviderShellHelper providerShellHelper, boolean useLegacyApis)653 private void reportUncertain(FakeTimeZoneProviderShellHelper providerShellHelper, 654 boolean useLegacyApis) throws Exception { 655 if (useLegacyApis) { 656 providerShellHelper.reportUncertainLegacy(); 657 } else { 658 providerShellHelper.reportUncertain( 659 DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE, DEPENDENCY_STATUS_OK, 660 OPERATION_STATUS_OK); 661 } 662 } 663 } 664