diff options
6 files changed, 867 insertions, 895 deletions
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java deleted file mode 100644 index b9da2ebd3663..000000000000 --- a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java +++ /dev/null @@ -1,670 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.timezonedetector.location; - -import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE; -import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION; -import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN; - -import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog; -import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog; -import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState; -import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; -import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; -import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; -import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; -import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; -import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; - -import android.annotation.DurationMillisLong; -import android.annotation.ElapsedRealtimeLong; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.service.timezone.TimeZoneProviderEvent; -import android.service.timezone.TimeZoneProviderSuggestion; -import android.util.IndentingPrintWriter; - -import com.android.internal.annotations.GuardedBy; -import com.android.server.timezonedetector.ConfigurationInternal; -import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion; -import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue; - -import java.time.Duration; -import java.util.Objects; - -/** - * A real implementation of {@link LocationTimeZoneProviderController} that supports a primary and a - * secondary {@link LocationTimeZoneProvider}. - * - * <p>The primary is used until it fails or becomes uncertain. The secondary will then be started. - * The controller will immediately make suggestions based on "certain" {@link - * TimeZoneProviderEvent}s, i.e. events that demonstrate the provider is certain what the time zone - * is. The controller will not make immediate suggestions based on "uncertain" events, giving - * providers time to change their mind. This also gives the secondary provider time to initialize - * when the primary becomes uncertain. - */ -class ControllerImpl extends LocationTimeZoneProviderController { - - @NonNull private final LocationTimeZoneProvider mPrimaryProvider; - - @NonNull private final LocationTimeZoneProvider mSecondaryProvider; - - @GuardedBy("mSharedLock") - // Non-null after initialize() - private ConfigurationInternal mCurrentUserConfiguration; - - @GuardedBy("mSharedLock") - // Non-null after initialize() - private Environment mEnvironment; - - @GuardedBy("mSharedLock") - // Non-null after initialize() - private Callback mCallback; - - /** Indicates both providers have completed initialization. */ - @GuardedBy("mSharedLock") - private boolean mProvidersInitialized; - - /** - * Used for scheduling uncertainty timeouts, i.e after a provider has reported uncertainty. - * This timeout is not provider-specific: it is started when the controller becomes uncertain - * due to events it has received from one or other provider. - */ - @NonNull private final SingleRunnableQueue mUncertaintyTimeoutQueue; - - /** Contains the last suggestion actually made, if there is one. */ - @GuardedBy("mSharedLock") - @Nullable - private GeolocationTimeZoneSuggestion mLastSuggestion; - - ControllerImpl(@NonNull ThreadingDomain threadingDomain, - @NonNull LocationTimeZoneProvider primaryProvider, - @NonNull LocationTimeZoneProvider secondaryProvider) { - super(threadingDomain); - mUncertaintyTimeoutQueue = threadingDomain.createSingleRunnableQueue(); - mPrimaryProvider = Objects.requireNonNull(primaryProvider); - mSecondaryProvider = Objects.requireNonNull(secondaryProvider); - } - - @Override - void initialize(@NonNull Environment environment, @NonNull Callback callback) { - mThreadingDomain.assertCurrentThread(); - - synchronized (mSharedLock) { - debugLog("initialize()"); - mEnvironment = Objects.requireNonNull(environment); - mCallback = Objects.requireNonNull(callback); - mCurrentUserConfiguration = environment.getCurrentUserConfigurationInternal(); - - LocationTimeZoneProvider.ProviderListener providerListener = - ControllerImpl.this::onProviderStateChange; - mPrimaryProvider.initialize(providerListener); - mSecondaryProvider.initialize(providerListener); - mProvidersInitialized = true; - - alterProvidersStartedStateIfRequired( - null /* oldConfiguration */, mCurrentUserConfiguration); - } - } - - @Override - void onConfigurationInternalChanged() { - mThreadingDomain.assertCurrentThread(); - - synchronized (mSharedLock) { - debugLog("onConfigChanged()"); - - ConfigurationInternal oldConfig = mCurrentUserConfiguration; - ConfigurationInternal newConfig = mEnvironment.getCurrentUserConfigurationInternal(); - mCurrentUserConfiguration = newConfig; - - if (!newConfig.equals(oldConfig)) { - if (newConfig.getUserId() != oldConfig.getUserId()) { - // If the user changed, stop the providers if needed. They may be re-started - // for the new user immediately afterwards if their settings allow. - debugLog("User changed. old=" + oldConfig.getUserId() - + ", new=" + newConfig.getUserId() + ": Stopping providers"); - stopProviders(); - - alterProvidersStartedStateIfRequired(null /* oldConfiguration */, newConfig); - } else { - alterProvidersStartedStateIfRequired(oldConfig, newConfig); - } - } - } - } - - @Override - boolean isUncertaintyTimeoutSet() { - return mUncertaintyTimeoutQueue.hasQueued(); - } - - @Override - @DurationMillisLong - long getUncertaintyTimeoutDelayMillis() { - return mUncertaintyTimeoutQueue.getQueuedDelayMillis(); - } - - @Override - void destroy() { - mThreadingDomain.assertCurrentThread(); - - synchronized (mSharedLock) { - stopProviders(); - mPrimaryProvider.destroy(); - mSecondaryProvider.destroy(); - } - } - - @GuardedBy("mSharedLock") - private void stopProviders() { - stopProviderIfStarted(mPrimaryProvider); - stopProviderIfStarted(mSecondaryProvider); - - // By definition, if both providers are stopped, the controller is uncertain. - cancelUncertaintyTimeout(); - - // If a previous "certain" suggestion has been made, then a new "uncertain" - // suggestion must now be made to indicate the controller {does not / no longer has} - // an opinion and will not be sending further updates (until at least the providers are - // re-started). - if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) { - GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( - mEnvironment.elapsedRealtimeMillis(), "Providers are stopping"); - makeSuggestion(suggestion); - } - } - - @GuardedBy("mSharedLock") - private void stopProviderIfStarted(@NonNull LocationTimeZoneProvider provider) { - if (provider.getCurrentState().isStarted()) { - stopProvider(provider); - } - } - - @GuardedBy("mSharedLock") - private void stopProvider(@NonNull LocationTimeZoneProvider provider) { - ProviderState providerState = provider.getCurrentState(); - switch (providerState.stateEnum) { - case PROVIDER_STATE_STOPPED: { - debugLog("No need to stop " + provider + ": already stopped"); - break; - } - case PROVIDER_STATE_STARTED_INITIALIZING: - case PROVIDER_STATE_STARTED_CERTAIN: - case PROVIDER_STATE_STARTED_UNCERTAIN: { - debugLog("Stopping " + provider); - provider.stopUpdates(); - break; - } - case PROVIDER_STATE_PERM_FAILED: - case PROVIDER_STATE_DESTROYED: { - debugLog("Unable to stop " + provider + ": it is terminated."); - break; - } - default: { - warnLog("Unknown provider state: " + provider); - break; - } - } - } - - /** - * Sets the providers into the correct started/stopped state for the {@code newConfiguration} - * and, if there is a provider state change, makes any suggestions required to inform the - * downstream time zone detection code. - * - * <p>This is a utility method that exists to avoid duplicated logic for the various cases when - * provider started / stopped state may need to be set or changed, e.g. during initialization - * or when a new configuration has been received. - */ - @GuardedBy("mSharedLock") - private void alterProvidersStartedStateIfRequired( - @Nullable ConfigurationInternal oldConfiguration, - @NonNull ConfigurationInternal newConfiguration) { - - // Provider started / stopped states only need to be changed if geoDetectionEnabled has - // changed. - boolean oldGeoDetectionEnabled = oldConfiguration != null - && oldConfiguration.getGeoDetectionEnabledBehavior(); - boolean newGeoDetectionEnabled = newConfiguration.getGeoDetectionEnabledBehavior(); - if (oldGeoDetectionEnabled == newGeoDetectionEnabled) { - return; - } - - // The check above ensures that the logic below only executes if providers are going from - // {started *} -> {stopped}, or {stopped} -> {started initializing}. If this changes in - // future and there could be {started *} -> {started *} cases, or cases where the provider - // can't be assumed to go straight to the {started initializing} state, then the logic below - // would need to cover extra conditions, for example: - // 1) If the primary is in {started uncertain}, the secondary should be started. - // 2) If (1), and the secondary instantly enters the {perm failed} state, the uncertainty - // timeout started when the primary entered {started uncertain} should be cancelled. - - if (newGeoDetectionEnabled) { - // Try to start the primary provider. - tryStartProvider(mPrimaryProvider, newConfiguration); - - // The secondary should only ever be started if the primary now isn't started (i.e. it - // couldn't become {started initializing} because it is {perm failed}). - ProviderState newPrimaryState = mPrimaryProvider.getCurrentState(); - if (!newPrimaryState.isStarted()) { - // If the primary provider is {perm failed} then the controller must try to start - // the secondary. - tryStartProvider(mSecondaryProvider, newConfiguration); - - ProviderState newSecondaryState = mSecondaryProvider.getCurrentState(); - if (!newSecondaryState.isStarted()) { - // If both providers are {perm failed} then the controller immediately - // becomes uncertain. - GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( - mEnvironment.elapsedRealtimeMillis(), - "Providers are failed:" - + " primary=" + mPrimaryProvider.getCurrentState() - + " secondary=" + mPrimaryProvider.getCurrentState()); - makeSuggestion(suggestion); - } - } - } else { - stopProviders(); - } - } - - @GuardedBy("mSharedLock") - private void tryStartProvider(@NonNull LocationTimeZoneProvider provider, - @NonNull ConfigurationInternal configuration) { - ProviderState providerState = provider.getCurrentState(); - switch (providerState.stateEnum) { - case PROVIDER_STATE_STOPPED: { - debugLog("Enabling " + provider); - provider.startUpdates(configuration, - mEnvironment.getProviderInitializationTimeout(), - mEnvironment.getProviderInitializationTimeoutFuzz(), - mEnvironment.getProviderEventFilteringAgeThreshold()); - break; - } - case PROVIDER_STATE_STARTED_INITIALIZING: - case PROVIDER_STATE_STARTED_CERTAIN: - case PROVIDER_STATE_STARTED_UNCERTAIN: { - debugLog("No need to start " + provider + ": already started"); - break; - } - case PROVIDER_STATE_PERM_FAILED: - case PROVIDER_STATE_DESTROYED: { - debugLog("Unable to start " + provider + ": it is terminated"); - break; - } - default: { - throw new IllegalStateException("Unknown provider state:" - + " provider=" + provider); - } - } - } - - void onProviderStateChange(@NonNull ProviderState providerState) { - mThreadingDomain.assertCurrentThread(); - LocationTimeZoneProvider provider = providerState.provider; - assertProviderKnown(provider); - - synchronized (mSharedLock) { - // Ignore provider state changes during initialization. e.g. if the primary provider - // moves to PROVIDER_STATE_PERM_FAILED during initialization, the secondary will not - // be ready to take over yet. - if (!mProvidersInitialized) { - warnLog("onProviderStateChange: Ignoring provider state change because both" - + " providers have not yet completed initialization." - + " providerState=" + providerState); - return; - } - - switch (providerState.stateEnum) { - case PROVIDER_STATE_STARTED_INITIALIZING: - case PROVIDER_STATE_STOPPED: - case PROVIDER_STATE_DESTROYED: { - // This should never happen: entering initializing, stopped or destroyed are - // triggered by the controller so and should not trigger a state change - // callback. - warnLog("onProviderStateChange: Unexpected state change for provider," - + " provider=" + provider); - break; - } - case PROVIDER_STATE_STARTED_CERTAIN: - case PROVIDER_STATE_STARTED_UNCERTAIN: { - // These are valid and only happen if an event is received while the provider is - // started. - debugLog("onProviderStateChange: Received notification of a state change while" - + " started, provider=" + provider); - handleProviderStartedStateChange(providerState); - break; - } - case PROVIDER_STATE_PERM_FAILED: { - debugLog("Received notification of permanent failure for" - + " provider=" + provider); - handleProviderFailedStateChange(providerState); - break; - } - default: { - warnLog("onProviderStateChange: Unexpected provider=" + provider); - } - } - } - } - - private void assertProviderKnown(@NonNull LocationTimeZoneProvider provider) { - if (provider != mPrimaryProvider && provider != mSecondaryProvider) { - throw new IllegalArgumentException("Unknown provider: " + provider); - } - } - - /** - * Called when a provider has reported that it has failed permanently. - */ - @GuardedBy("mSharedLock") - private void handleProviderFailedStateChange(@NonNull ProviderState providerState) { - LocationTimeZoneProvider failedProvider = providerState.provider; - ProviderState primaryCurrentState = mPrimaryProvider.getCurrentState(); - ProviderState secondaryCurrentState = mSecondaryProvider.getCurrentState(); - - // If a provider has failed, the other may need to be started. - if (failedProvider == mPrimaryProvider) { - if (!secondaryCurrentState.isTerminated()) { - // Try to start the secondary. This does nothing if the provider is already - // started, and will leave the provider in {started initializing} if the provider is - // stopped. - tryStartProvider(mSecondaryProvider, mCurrentUserConfiguration); - } - } else if (failedProvider == mSecondaryProvider) { - // No-op: The secondary will only be active if the primary is uncertain or is - // terminated. So, there the primary should not need to be started when the secondary - // fails. - if (primaryCurrentState.stateEnum != PROVIDER_STATE_STARTED_UNCERTAIN - && !primaryCurrentState.isTerminated()) { - warnLog("Secondary provider unexpected reported a failure:" - + " failed provider=" + failedProvider.getName() - + ", primary provider=" + mPrimaryProvider - + ", secondary provider=" + mSecondaryProvider); - } - } - - // If both providers are now terminated, the controller needs to tell the next component in - // the time zone detection process. - if (primaryCurrentState.isTerminated() && secondaryCurrentState.isTerminated()) { - - // If both providers are newly terminated then the controller is uncertain by definition - // and it will never recover so it can send a suggestion immediately. - cancelUncertaintyTimeout(); - - // If both providers are now terminated, then a suggestion must be sent informing the - // time zone detector that there are no further updates coming in future. - GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( - mEnvironment.elapsedRealtimeMillis(), - "Both providers are terminated:" - + " primary=" + primaryCurrentState.provider - + ", secondary=" + secondaryCurrentState.provider); - makeSuggestion(suggestion); - } - } - - /** - * Called when a provider has changed state but just moved from one started state to another - * started state, usually as a result of a new {@link TimeZoneProviderEvent} being received. - * However, there are rare cases where the event can also be null. - */ - @GuardedBy("mSharedLock") - private void handleProviderStartedStateChange(@NonNull ProviderState providerState) { - LocationTimeZoneProvider provider = providerState.provider; - TimeZoneProviderEvent event = providerState.event; - if (event == null) { - // Implicit uncertainty, i.e. where the provider is started, but a problem has been - // detected without having received an event. For example, if the process has detected - // the loss of a binder-based provider, or initialization took too long. This is treated - // the same as explicit uncertainty, i.e. where the provider has explicitly told this - // process it is uncertain. - long uncertaintyStartedElapsedMillis = mEnvironment.elapsedRealtimeMillis(); - handleProviderUncertainty(provider, uncertaintyStartedElapsedMillis, - "provider=" + provider + ", implicit uncertainty, event=null"); - return; - } - - if (!mCurrentUserConfiguration.getGeoDetectionEnabledBehavior()) { - // This should not happen: the provider should not be in an started state if the user - // does not have geodetection enabled. - warnLog("Provider=" + provider + " is started, but" - + " currentUserConfiguration=" + mCurrentUserConfiguration - + " suggests it shouldn't be."); - } - - switch (event.getType()) { - case EVENT_TYPE_PERMANENT_FAILURE: { - // This shouldn't happen. A provider cannot be started and have this event type. - warnLog("Provider=" + provider + " is started, but event suggests it shouldn't be"); - break; - } - case EVENT_TYPE_UNCERTAIN: { - long uncertaintyStartedElapsedMillis = event.getCreationElapsedMillis(); - handleProviderUncertainty(provider, uncertaintyStartedElapsedMillis, - "provider=" + provider + ", explicit uncertainty. event=" + event); - break; - } - case EVENT_TYPE_SUGGESTION: { - handleProviderSuggestion(provider, event); - break; - } - default: { - warnLog("Unknown eventType=" + event.getType()); - break; - } - } - } - - /** - * Called when a provider has become "certain" about the time zone(s). - */ - @GuardedBy("mSharedLock") - private void handleProviderSuggestion( - @NonNull LocationTimeZoneProvider provider, - @NonNull TimeZoneProviderEvent providerEvent) { - - // By definition, the controller is now certain. - cancelUncertaintyTimeout(); - - if (provider == mPrimaryProvider) { - stopProviderIfStarted(mSecondaryProvider); - } - - TimeZoneProviderSuggestion providerSuggestion = providerEvent.getSuggestion(); - - // For the suggestion's effectiveFromElapsedMillis, use the time embedded in the provider's - // suggestion (which indicates the time when the provider detected the location used to - // establish the time zone). - // - // An alternative would be to use the current time or the providerEvent creation time, but - // this would hinder the ability for the time_zone_detector to judge which suggestions are - // based on newer information when comparing suggestions between different sources. - long effectiveFromElapsedMillis = providerSuggestion.getElapsedRealtimeMillis(); - GeolocationTimeZoneSuggestion geoSuggestion = - GeolocationTimeZoneSuggestion.createCertainSuggestion( - effectiveFromElapsedMillis, providerSuggestion.getTimeZoneIds()); - - String debugInfo = "Event received provider=" + provider - + ", providerEvent=" + providerEvent - + ", suggestionCreationTime=" + mEnvironment.elapsedRealtimeMillis(); - geoSuggestion.addDebugInfo(debugInfo); - makeSuggestion(geoSuggestion); - } - - @Override - public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) { - synchronized (mSharedLock) { - ipw.println("LocationTimeZoneProviderController:"); - - ipw.increaseIndent(); // level 1 - ipw.println("mCurrentUserConfiguration=" + mCurrentUserConfiguration); - ipw.println("providerInitializationTimeout=" - + mEnvironment.getProviderInitializationTimeout()); - ipw.println("providerInitializationTimeoutFuzz=" - + mEnvironment.getProviderInitializationTimeoutFuzz()); - ipw.println("uncertaintyDelay=" + mEnvironment.getUncertaintyDelay()); - ipw.println("mLastSuggestion=" + mLastSuggestion); - - ipw.println("Primary Provider:"); - ipw.increaseIndent(); // level 2 - mPrimaryProvider.dump(ipw, args); - ipw.decreaseIndent(); // level 2 - - ipw.println("Secondary Provider:"); - ipw.increaseIndent(); // level 2 - mSecondaryProvider.dump(ipw, args); - ipw.decreaseIndent(); // level 2 - - ipw.decreaseIndent(); // level 1 - } - } - - /** Sends an immediate suggestion, updating mLastSuggestion. */ - @GuardedBy("mSharedLock") - private void makeSuggestion(@NonNull GeolocationTimeZoneSuggestion suggestion) { - debugLog("makeSuggestion: suggestion=" + suggestion); - mCallback.suggest(suggestion); - mLastSuggestion = suggestion; - } - - /** Clears the uncertainty timeout. */ - @GuardedBy("mSharedLock") - private void cancelUncertaintyTimeout() { - mUncertaintyTimeoutQueue.cancel(); - } - - /** - * Called when a provider has become "uncertain" about the time zone. - * - * <p>A provider is expected to report its uncertainty as soon as it becomes uncertain, as - * this enables the most flexibility for the controller to start other providers when there are - * multiple ones available. The controller is therefore responsible for deciding when to make a - * "uncertain" suggestion to the downstream time zone detector. - * - * <p>This method schedules an "uncertainty" timeout (if one isn't already scheduled) to be - * triggered later if nothing else preempts it. It can be preempted if the provider becomes - * certain (or does anything else that calls {@link - * #makeSuggestion(GeolocationTimeZoneSuggestion)}) within {@link - * Environment#getUncertaintyDelay()}. Preemption causes the scheduled - * "uncertainty" timeout to be cancelled. If the provider repeatedly sends uncertainty events - * within the uncertainty delay period, those events are effectively ignored (i.e. the timeout - * is not reset each time). - */ - @GuardedBy("mSharedLock") - void handleProviderUncertainty( - @NonNull LocationTimeZoneProvider provider, - @ElapsedRealtimeLong long uncertaintyStartedElapsedMillis, - @NonNull String reason) { - Objects.requireNonNull(provider); - - // Start the uncertainty timeout if needed to ensure the controller will eventually make an - // uncertain suggestion if no success event arrives in time to counteract it. - if (!mUncertaintyTimeoutQueue.hasQueued()) { - debugLog("Starting uncertainty timeout: reason=" + reason); - - Duration uncertaintyDelay = mEnvironment.getUncertaintyDelay(); - mUncertaintyTimeoutQueue.runDelayed( - () -> onProviderUncertaintyTimeout( - provider, uncertaintyStartedElapsedMillis, uncertaintyDelay), - uncertaintyDelay.toMillis()); - } - - if (provider == mPrimaryProvider) { - // (Try to) start the secondary. It could already be started, or enabling might not - // succeed if the provider has previously reported it is perm failed. The uncertainty - // timeout (set above) is used to ensure that an uncertain suggestion will be made if - // the secondary cannot generate a success event in time. - tryStartProvider(mSecondaryProvider, mCurrentUserConfiguration); - } - } - - private void onProviderUncertaintyTimeout( - @NonNull LocationTimeZoneProvider provider, - @ElapsedRealtimeLong long uncertaintyStartedElapsedMillis, - @NonNull Duration uncertaintyDelay) { - mThreadingDomain.assertCurrentThread(); - - synchronized (mSharedLock) { - long afterUncertaintyTimeoutElapsedMillis = mEnvironment.elapsedRealtimeMillis(); - - // For the effectiveFromElapsedMillis suggestion property, use the - // uncertaintyStartedElapsedMillis. This is the time when the provider first reported - // uncertainty, i.e. before the uncertainty timeout. - // - // afterUncertaintyTimeoutElapsedMillis could be used instead, which is the time when - // the location_time_zone_manager finally confirms that the time zone was uncertain, - // but the suggestion property allows the information to be back-dated, which should - // help when comparing suggestions from different sources. - GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( - uncertaintyStartedElapsedMillis, - "Uncertainty timeout triggered for " + provider.getName() + ":" - + " primary=" + mPrimaryProvider - + ", secondary=" + mSecondaryProvider - + ", uncertaintyStarted=" - + Duration.ofMillis(uncertaintyStartedElapsedMillis) - + ", afterUncertaintyTimeout=" - + Duration.ofMillis(afterUncertaintyTimeoutElapsedMillis) - + ", uncertaintyDelay=" + uncertaintyDelay - ); - makeSuggestion(suggestion); - } - } - - @NonNull - private static GeolocationTimeZoneSuggestion createUncertainSuggestion( - @ElapsedRealtimeLong long effectiveFromElapsedMillis, - @NonNull String reason) { - GeolocationTimeZoneSuggestion suggestion = - GeolocationTimeZoneSuggestion.createUncertainSuggestion( - effectiveFromElapsedMillis); - suggestion.addDebugInfo(reason); - return suggestion; - } - - /** - * Clears recorded provider state changes (for use during tests). - */ - void clearRecordedProviderStates() { - mThreadingDomain.assertCurrentThread(); - - synchronized (mSharedLock) { - mPrimaryProvider.clearRecordedStates(); - mSecondaryProvider.clearRecordedStates(); - } - } - - /** - * Returns a snapshot of the current controller state for tests. - */ - @NonNull - LocationTimeZoneManagerServiceState getStateForTests() { - mThreadingDomain.assertCurrentThread(); - - synchronized (mSharedLock) { - LocationTimeZoneManagerServiceState.Builder builder = - new LocationTimeZoneManagerServiceState.Builder(); - if (mLastSuggestion != null) { - builder.setLastSuggestion(mLastSuggestion); - } - builder.setPrimaryProviderStateChanges(mPrimaryProvider.getRecordedStates()) - .setSecondaryProviderStateChanges(mSecondaryProvider.getRecordedStates()); - return builder.build(); - } - } -} diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java index ddbeac4e458a..af8cf6e05a84 100644 --- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java @@ -147,16 +147,16 @@ public class LocationTimeZoneManagerService extends Binder { /** The shared lock from {@link #mThreadingDomain}. */ @NonNull private final Object mSharedLock; - @NonNull - private final ServiceConfigAccessor mServiceConfigAccessor; + @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; // Lazily initialized. Can be null if the service has been stopped. @GuardedBy("mSharedLock") - private ControllerImpl mLocationTimeZoneDetectorController; + private LocationTimeZoneProviderController mLocationTimeZoneProviderController; // Lazily initialized. Can be null if the service has been stopped. @GuardedBy("mSharedLock") - private ControllerEnvironmentImpl mEnvironment; + private LocationTimeZoneProviderControllerEnvironmentImpl + mLocationTimeZoneProviderControllerEnvironment; LocationTimeZoneManagerService(@NonNull Context context, @NonNull ServiceConfigAccessor serviceConfigAccessor) { @@ -190,7 +190,7 @@ public class LocationTimeZoneManagerService extends Binder { // Avoid starting the service if it is currently stopped. This is required because // server flags are used by tests to set behavior with the service stopped, and we don't // want the service being restarted after each flag is set. - if (mLocationTimeZoneDetectorController != null) { + if (mLocationTimeZoneProviderController != null) { // Stop and start the service, waiting until completion. stopOnDomainThread(); startOnDomainThread(); @@ -278,19 +278,22 @@ public class LocationTimeZoneManagerService extends Binder { return; } - if (mLocationTimeZoneDetectorController == null) { + if (mLocationTimeZoneProviderController == null) { LocationTimeZoneProvider primary = mPrimaryProviderConfig.createProvider(); LocationTimeZoneProvider secondary = mSecondaryProviderConfig.createProvider(); - ControllerImpl controller = - new ControllerImpl(mThreadingDomain, primary, secondary); - ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl( - mThreadingDomain, mServiceConfigAccessor, controller); - ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain); + LocationTimeZoneProviderController controller = + new LocationTimeZoneProviderController( + mThreadingDomain, primary, secondary); + LocationTimeZoneProviderControllerEnvironmentImpl environment = + new LocationTimeZoneProviderControllerEnvironmentImpl( + mThreadingDomain, mServiceConfigAccessor, controller); + LocationTimeZoneProviderControllerCallbackImpl callback = + new LocationTimeZoneProviderControllerCallbackImpl(mThreadingDomain); controller.initialize(environment, callback); - mEnvironment = environment; - mLocationTimeZoneDetectorController = controller; + mLocationTimeZoneProviderControllerEnvironment = environment; + mLocationTimeZoneProviderController = controller; } } } @@ -312,11 +315,11 @@ public class LocationTimeZoneManagerService extends Binder { mThreadingDomain.assertCurrentThread(); synchronized (mSharedLock) { - if (mLocationTimeZoneDetectorController != null) { - mLocationTimeZoneDetectorController.destroy(); - mLocationTimeZoneDetectorController = null; - mEnvironment.destroy(); - mEnvironment = null; + if (mLocationTimeZoneProviderController != null) { + mLocationTimeZoneProviderController.destroy(); + mLocationTimeZoneProviderController = null; + mLocationTimeZoneProviderControllerEnvironment.destroy(); + mLocationTimeZoneProviderControllerEnvironment = null; // Clear test state so it won't be used the next time the service is started. mServiceConfigAccessor.resetVolatileTestConfig(); @@ -338,8 +341,8 @@ public class LocationTimeZoneManagerService extends Binder { mThreadingDomain.postAndWait(() -> { synchronized (mSharedLock) { - if (mLocationTimeZoneDetectorController != null) { - mLocationTimeZoneDetectorController.clearRecordedProviderStates(); + if (mLocationTimeZoneProviderController != null) { + mLocationTimeZoneProviderController.clearRecordedProviderStates(); } } }, BLOCKING_OP_WAIT_DURATION_MILLIS); @@ -357,10 +360,10 @@ public class LocationTimeZoneManagerService extends Binder { return mThreadingDomain.postAndWait( () -> { synchronized (mSharedLock) { - if (mLocationTimeZoneDetectorController == null) { + if (mLocationTimeZoneProviderController == null) { return null; } - return mLocationTimeZoneDetectorController.getStateForTests(); + return mLocationTimeZoneProviderController.getStateForTests(); } }, BLOCKING_OP_WAIT_DURATION_MILLIS); @@ -390,10 +393,10 @@ public class LocationTimeZoneManagerService extends Binder { mSecondaryProviderConfig.dump(ipw, args); ipw.decreaseIndent(); - if (mLocationTimeZoneDetectorController == null) { + if (mLocationTimeZoneProviderController == null) { ipw.println("{Stopped}"); } else { - mLocationTimeZoneDetectorController.dump(ipw, args); + mLocationTimeZoneProviderController.dump(ipw, args); } ipw.decreaseIndent(); } @@ -450,7 +453,6 @@ public class LocationTimeZoneManagerService extends Binder { mServiceConfigAccessor.getRecordProviderStateChanges()); } - @GuardedBy("mSharedLock") @Override public void dump(IndentingPrintWriter ipw, String[] args) { ipw.printf("getMode()=%s\n", getMode()); diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java index 4dff02e8ab6f..dbd3877debfe 100644 --- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java @@ -16,31 +16,57 @@ package com.android.server.timezonedetector.location; +import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE; +import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION; +import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN; + +import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog; +import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; +import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; + import android.annotation.DurationMillisLong; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; -import android.os.Handler; +import android.annotation.Nullable; +import android.service.timezone.TimeZoneProviderEvent; +import android.service.timezone.TimeZoneProviderSuggestion; +import android.util.IndentingPrintWriter; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.timezonedetector.ConfigurationInternal; import com.android.server.timezonedetector.Dumpable; import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion; -import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState; +import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue; import java.time.Duration; import java.util.Objects; /** - * An base class for the component responsible handling events from {@link - * LocationTimeZoneProvider}s and synthesizing time zone ID suggestions for sending to the time zone - * detector. This interface primarily exists to extract testable detection logic, i.e. with - * a minimal number of threading considerations or dependencies on Android infrastructure. + * The component responsible handling events from {@link LocationTimeZoneProvider}s and synthesizing + * time zone ID suggestions for sending to the time zone detector. + * + * <p>This class primarily exists to extract unit-testable logic from the surrounding service class, + * i.e. with a minimal number of threading considerations or direct dependencies on Android + * infrastructure. + * + * <p>This class supports a primary and a secondary {@link LocationTimeZoneProvider}. The primary is + * used until it fails or becomes uncertain. The secondary will then be started. The controller will + * immediately make suggestions based on "certain" {@link TimeZoneProviderEvent}s, i.e. events that + * demonstrate the provider is certain what the time zone is. The controller will not make immediate + * suggestions based on "uncertain" events, giving providers time to change their mind. This also + * gives the secondary provider time to initialize when the primary becomes uncertain. * * <p>The controller interacts with the following components: * <ul> - * <li>The surrounding service, which calls {@link #initialize(Environment, Callback)} and - * {@link #onConfigurationInternalChanged()}.</li> - * <li>The {@link Environment} through which obtains information it needs.</li> + * <li>The surrounding service, which calls {@link #initialize(Environment, Callback)}. + * <li>The {@link Environment} through which it obtains information it needs.</li> * <li>The {@link Callback} through which it makes time zone suggestions.</li> * <li>Any {@link LocationTimeZoneProvider} instances it owns, which communicate via the * {@link LocationTimeZoneProvider.ProviderListener#onProviderStateChange(ProviderState)} @@ -49,8 +75,9 @@ import java.util.Objects; * * <p>All incoming calls except for {@link * LocationTimeZoneProviderController#dump(android.util.IndentingPrintWriter, String[])} must be - * made on the {@link Handler} thread of the {@link ThreadingDomain} passed to {@link - * #LocationTimeZoneProviderController(ThreadingDomain)}. + * made on the {@link android.os.Handler} thread of the {@link ThreadingDomain} passed to {@link + * #LocationTimeZoneProviderController(ThreadingDomain, LocationTimeZoneProvider, + * LocationTimeZoneProvider)}. * * <p>Provider / controller integration notes: * @@ -59,43 +86,631 @@ import java.util.Objects; * different from the certainty that there are no time zone IDs for the current location. A provider * can be certain about there being no time zone IDs for a location for good reason, e.g. for * disputed areas and oceans. Distinguishing uncertainty allows the controller to try other - * providers (or give up), where as certainty means it should not. + * providers (or give up), whereas certainty means it should not. * * <p>A provider can fail permanently. A permanent failure will stop the provider until next * boot. */ -abstract class LocationTimeZoneProviderController implements Dumpable { +class LocationTimeZoneProviderController implements Dumpable { + + @NonNull private final ThreadingDomain mThreadingDomain; + @NonNull private final Object mSharedLock; + /** + * Used for scheduling uncertainty timeouts, i.e. after a provider has reported uncertainty. + * This timeout is not provider-specific: it is started when the controller becomes uncertain + * due to events it has received from one or other provider. + */ + @NonNull private final SingleRunnableQueue mUncertaintyTimeoutQueue; + + @NonNull private final LocationTimeZoneProvider mPrimaryProvider; + @NonNull private final LocationTimeZoneProvider mSecondaryProvider; + + @GuardedBy("mSharedLock") + // Non-null after initialize() + private ConfigurationInternal mCurrentUserConfiguration; - @NonNull protected final ThreadingDomain mThreadingDomain; - @NonNull protected final Object mSharedLock; + @GuardedBy("mSharedLock") + // Non-null after initialize() + private Environment mEnvironment; - LocationTimeZoneProviderController(@NonNull ThreadingDomain threadingDomain) { + @GuardedBy("mSharedLock") + // Non-null after initialize() + private Callback mCallback; + + /** Indicates both providers have completed initialization. */ + @GuardedBy("mSharedLock") + private boolean mProvidersInitialized; + + + /** Contains the last suggestion actually made, if there is one. */ + @GuardedBy("mSharedLock") + @Nullable + private GeolocationTimeZoneSuggestion mLastSuggestion; + + LocationTimeZoneProviderController(@NonNull ThreadingDomain threadingDomain, + @NonNull LocationTimeZoneProvider primaryProvider, + @NonNull LocationTimeZoneProvider secondaryProvider) { mThreadingDomain = Objects.requireNonNull(threadingDomain); mSharedLock = threadingDomain.getLockObject(); + mUncertaintyTimeoutQueue = threadingDomain.createSingleRunnableQueue(); + mPrimaryProvider = Objects.requireNonNull(primaryProvider); + mSecondaryProvider = Objects.requireNonNull(secondaryProvider); } /** * Called to initialize the controller during boot. Called once only. * {@link LocationTimeZoneProvider#initialize} must be called by this method. */ - abstract void initialize(@NonNull Environment environment, @NonNull Callback callback); + void initialize(@NonNull Environment environment, @NonNull Callback callback) { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + debugLog("initialize()"); + mEnvironment = Objects.requireNonNull(environment); + mCallback = Objects.requireNonNull(callback); + mCurrentUserConfiguration = environment.getCurrentUserConfigurationInternal(); + + LocationTimeZoneProvider.ProviderListener providerListener = + LocationTimeZoneProviderController.this::onProviderStateChange; + mPrimaryProvider.initialize(providerListener); + mSecondaryProvider.initialize(providerListener); + mProvidersInitialized = true; + + alterProvidersStartedStateIfRequired( + null /* oldConfiguration */, mCurrentUserConfiguration); + } + } /** * Called when the content of the {@link ConfigurationInternal} may have changed. The receiver * should call {@link Environment#getCurrentUserConfigurationInternal()} to get the current * user's config. This call must be made on the {@link ThreadingDomain} handler thread. */ - abstract void onConfigurationInternalChanged(); + void onConfigurationInternalChanged() { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + debugLog("onConfigChanged()"); + + ConfigurationInternal oldConfig = mCurrentUserConfiguration; + ConfigurationInternal newConfig = mEnvironment.getCurrentUserConfigurationInternal(); + mCurrentUserConfiguration = newConfig; + + if (!newConfig.equals(oldConfig)) { + if (newConfig.getUserId() != oldConfig.getUserId()) { + // If the user changed, stop the providers if needed. They may be re-started + // for the new user immediately afterwards if their settings allow. + debugLog("User changed. old=" + oldConfig.getUserId() + + ", new=" + newConfig.getUserId() + ": Stopping providers"); + stopProviders(); + + alterProvidersStartedStateIfRequired(null /* oldConfiguration */, newConfig); + } else { + alterProvidersStartedStateIfRequired(oldConfig, newConfig); + } + } + } + } @VisibleForTesting - abstract boolean isUncertaintyTimeoutSet(); + boolean isUncertaintyTimeoutSet() { + return mUncertaintyTimeoutQueue.hasQueued(); + } @VisibleForTesting @DurationMillisLong - abstract long getUncertaintyTimeoutDelayMillis(); + long getUncertaintyTimeoutDelayMillis() { + return mUncertaintyTimeoutQueue.getQueuedDelayMillis(); + } /** Called if the geolocation time zone detection is being reconfigured. */ - abstract void destroy(); + void destroy() { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + stopProviders(); + mPrimaryProvider.destroy(); + mSecondaryProvider.destroy(); + } + } + + @GuardedBy("mSharedLock") + private void stopProviders() { + stopProviderIfStarted(mPrimaryProvider); + stopProviderIfStarted(mSecondaryProvider); + + // By definition, if both providers are stopped, the controller is uncertain. + cancelUncertaintyTimeout(); + + // If a previous "certain" suggestion has been made, then a new "uncertain" + // suggestion must now be made to indicate the controller {does not / no longer has} + // an opinion and will not be sending further updates (until at least the providers are + // re-started). + if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) { + GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( + mEnvironment.elapsedRealtimeMillis(), "Providers are stopping"); + makeSuggestion(suggestion); + } + } + + @GuardedBy("mSharedLock") + private void stopProviderIfStarted(@NonNull LocationTimeZoneProvider provider) { + if (provider.getCurrentState().isStarted()) { + stopProvider(provider); + } + } + + @GuardedBy("mSharedLock") + private void stopProvider(@NonNull LocationTimeZoneProvider provider) { + ProviderState providerState = provider.getCurrentState(); + switch (providerState.stateEnum) { + case PROVIDER_STATE_STOPPED: { + debugLog("No need to stop " + provider + ": already stopped"); + break; + } + case PROVIDER_STATE_STARTED_INITIALIZING: + case PROVIDER_STATE_STARTED_CERTAIN: + case PROVIDER_STATE_STARTED_UNCERTAIN: { + debugLog("Stopping " + provider); + provider.stopUpdates(); + break; + } + case PROVIDER_STATE_PERM_FAILED: + case PROVIDER_STATE_DESTROYED: { + debugLog("Unable to stop " + provider + ": it is terminated."); + break; + } + default: { + warnLog("Unknown provider state: " + provider); + break; + } + } + } + + /** + * Sets the providers into the correct started/stopped state for the {@code newConfiguration} + * and, if there is a provider state change, makes any suggestions required to inform the + * downstream time zone detection code. + * + * <p>This is a utility method that exists to avoid duplicated logic for the various cases when + * provider started / stopped state may need to be set or changed, e.g. during initialization + * or when a new configuration has been received. + */ + @GuardedBy("mSharedLock") + private void alterProvidersStartedStateIfRequired( + @Nullable ConfigurationInternal oldConfiguration, + @NonNull ConfigurationInternal newConfiguration) { + + // Provider started / stopped states only need to be changed if geoDetectionEnabled has + // changed. + boolean oldGeoDetectionEnabled = oldConfiguration != null + && oldConfiguration.getGeoDetectionEnabledBehavior(); + boolean newGeoDetectionEnabled = newConfiguration.getGeoDetectionEnabledBehavior(); + if (oldGeoDetectionEnabled == newGeoDetectionEnabled) { + return; + } + + // The check above ensures that the logic below only executes if providers are going from + // {started *} -> {stopped}, or {stopped} -> {started initializing}. If this changes in + // future and there could be {started *} -> {started *} cases, or cases where the provider + // can't be assumed to go straight to the {started initializing} state, then the logic below + // would need to cover extra conditions, for example: + // 1) If the primary is in {started uncertain}, the secondary should be started. + // 2) If (1), and the secondary instantly enters the {perm failed} state, the uncertainty + // timeout started when the primary entered {started uncertain} should be cancelled. + + if (newGeoDetectionEnabled) { + // Try to start the primary provider. + tryStartProvider(mPrimaryProvider, newConfiguration); + + // The secondary should only ever be started if the primary now isn't started (i.e. it + // couldn't become {started initializing} because it is {perm failed}). + ProviderState newPrimaryState = mPrimaryProvider.getCurrentState(); + if (!newPrimaryState.isStarted()) { + // If the primary provider is {perm failed} then the controller must try to start + // the secondary. + tryStartProvider(mSecondaryProvider, newConfiguration); + + ProviderState newSecondaryState = mSecondaryProvider.getCurrentState(); + if (!newSecondaryState.isStarted()) { + // If both providers are {perm failed} then the controller immediately + // becomes uncertain. + GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( + mEnvironment.elapsedRealtimeMillis(), + "Providers are failed:" + + " primary=" + mPrimaryProvider.getCurrentState() + + " secondary=" + mPrimaryProvider.getCurrentState()); + makeSuggestion(suggestion); + } + } + } else { + stopProviders(); + } + } + + @GuardedBy("mSharedLock") + private void tryStartProvider(@NonNull LocationTimeZoneProvider provider, + @NonNull ConfigurationInternal configuration) { + ProviderState providerState = provider.getCurrentState(); + switch (providerState.stateEnum) { + case PROVIDER_STATE_STOPPED: { + debugLog("Enabling " + provider); + provider.startUpdates(configuration, + mEnvironment.getProviderInitializationTimeout(), + mEnvironment.getProviderInitializationTimeoutFuzz(), + mEnvironment.getProviderEventFilteringAgeThreshold()); + break; + } + case PROVIDER_STATE_STARTED_INITIALIZING: + case PROVIDER_STATE_STARTED_CERTAIN: + case PROVIDER_STATE_STARTED_UNCERTAIN: { + debugLog("No need to start " + provider + ": already started"); + break; + } + case PROVIDER_STATE_PERM_FAILED: + case PROVIDER_STATE_DESTROYED: { + debugLog("Unable to start " + provider + ": it is terminated"); + break; + } + default: { + throw new IllegalStateException("Unknown provider state:" + + " provider=" + provider); + } + } + } + + void onProviderStateChange(@NonNull ProviderState providerState) { + mThreadingDomain.assertCurrentThread(); + LocationTimeZoneProvider provider = providerState.provider; + assertProviderKnown(provider); + + synchronized (mSharedLock) { + // Ignore provider state changes during initialization. e.g. if the primary provider + // moves to PROVIDER_STATE_PERM_FAILED during initialization, the secondary will not + // be ready to take over yet. + if (!mProvidersInitialized) { + warnLog("onProviderStateChange: Ignoring provider state change because both" + + " providers have not yet completed initialization." + + " providerState=" + providerState); + return; + } + + switch (providerState.stateEnum) { + case PROVIDER_STATE_STARTED_INITIALIZING: + case PROVIDER_STATE_STOPPED: + case PROVIDER_STATE_DESTROYED: { + // This should never happen: entering initializing, stopped or destroyed are + // triggered by the controller so and should not trigger a state change + // callback. + warnLog("onProviderStateChange: Unexpected state change for provider," + + " provider=" + provider); + break; + } + case PROVIDER_STATE_STARTED_CERTAIN: + case PROVIDER_STATE_STARTED_UNCERTAIN: { + // These are valid and only happen if an event is received while the provider is + // started. + debugLog("onProviderStateChange: Received notification of a state change while" + + " started, provider=" + provider); + handleProviderStartedStateChange(providerState); + break; + } + case PROVIDER_STATE_PERM_FAILED: { + debugLog("Received notification of permanent failure for" + + " provider=" + provider); + handleProviderFailedStateChange(providerState); + break; + } + default: { + warnLog("onProviderStateChange: Unexpected provider=" + provider); + } + } + } + } + + private void assertProviderKnown(@NonNull LocationTimeZoneProvider provider) { + if (provider != mPrimaryProvider && provider != mSecondaryProvider) { + throw new IllegalArgumentException("Unknown provider: " + provider); + } + } + + /** + * Called when a provider has reported that it has failed permanently. + */ + @GuardedBy("mSharedLock") + private void handleProviderFailedStateChange(@NonNull ProviderState providerState) { + LocationTimeZoneProvider failedProvider = providerState.provider; + ProviderState primaryCurrentState = mPrimaryProvider.getCurrentState(); + ProviderState secondaryCurrentState = mSecondaryProvider.getCurrentState(); + + // If a provider has failed, the other may need to be started. + if (failedProvider == mPrimaryProvider) { + if (!secondaryCurrentState.isTerminated()) { + // Try to start the secondary. This does nothing if the provider is already + // started, and will leave the provider in {started initializing} if the provider is + // stopped. + tryStartProvider(mSecondaryProvider, mCurrentUserConfiguration); + } + } else if (failedProvider == mSecondaryProvider) { + // No-op: The secondary will only be active if the primary is uncertain or is + // terminated. So, there the primary should not need to be started when the secondary + // fails. + if (primaryCurrentState.stateEnum != PROVIDER_STATE_STARTED_UNCERTAIN + && !primaryCurrentState.isTerminated()) { + warnLog("Secondary provider unexpected reported a failure:" + + " failed provider=" + failedProvider.getName() + + ", primary provider=" + mPrimaryProvider + + ", secondary provider=" + mSecondaryProvider); + } + } + + // If both providers are now terminated, the controller needs to tell the next component in + // the time zone detection process. + if (primaryCurrentState.isTerminated() && secondaryCurrentState.isTerminated()) { + + // If both providers are newly terminated then the controller is uncertain by definition + // and it will never recover so it can send a suggestion immediately. + cancelUncertaintyTimeout(); + + // If both providers are now terminated, then a suggestion must be sent informing the + // time zone detector that there are no further updates coming in future. + GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( + mEnvironment.elapsedRealtimeMillis(), + "Both providers are terminated:" + + " primary=" + primaryCurrentState.provider + + ", secondary=" + secondaryCurrentState.provider); + makeSuggestion(suggestion); + } + } + + /** + * Called when a provider has changed state but just moved from one started state to another + * started state, usually as a result of a new {@link TimeZoneProviderEvent} being received. + * However, there are rare cases where the event can also be null. + */ + @GuardedBy("mSharedLock") + private void handleProviderStartedStateChange(@NonNull ProviderState providerState) { + LocationTimeZoneProvider provider = providerState.provider; + TimeZoneProviderEvent event = providerState.event; + if (event == null) { + // Implicit uncertainty, i.e. where the provider is started, but a problem has been + // detected without having received an event. For example, if the process has detected + // the loss of a binder-based provider, or initialization took too long. This is treated + // the same as explicit uncertainty, i.e. where the provider has explicitly told this + // process it is uncertain. + long uncertaintyStartedElapsedMillis = mEnvironment.elapsedRealtimeMillis(); + handleProviderUncertainty(provider, uncertaintyStartedElapsedMillis, + "provider=" + provider + ", implicit uncertainty, event=null"); + return; + } + + if (!mCurrentUserConfiguration.getGeoDetectionEnabledBehavior()) { + // This should not happen: the provider should not be in an started state if the user + // does not have geodetection enabled. + warnLog("Provider=" + provider + " is started, but" + + " currentUserConfiguration=" + mCurrentUserConfiguration + + " suggests it shouldn't be."); + } + + switch (event.getType()) { + case EVENT_TYPE_PERMANENT_FAILURE: { + // This shouldn't happen. A provider cannot be started and have this event type. + warnLog("Provider=" + provider + " is started, but event suggests it shouldn't be"); + break; + } + case EVENT_TYPE_UNCERTAIN: { + long uncertaintyStartedElapsedMillis = event.getCreationElapsedMillis(); + handleProviderUncertainty(provider, uncertaintyStartedElapsedMillis, + "provider=" + provider + ", explicit uncertainty. event=" + event); + break; + } + case EVENT_TYPE_SUGGESTION: { + handleProviderSuggestion(provider, event); + break; + } + default: { + warnLog("Unknown eventType=" + event.getType()); + break; + } + } + } + + /** + * Called when a provider has become "certain" about the time zone(s). + */ + @GuardedBy("mSharedLock") + private void handleProviderSuggestion( + @NonNull LocationTimeZoneProvider provider, + @NonNull TimeZoneProviderEvent providerEvent) { + + // By definition, the controller is now certain. + cancelUncertaintyTimeout(); + + if (provider == mPrimaryProvider) { + stopProviderIfStarted(mSecondaryProvider); + } + + TimeZoneProviderSuggestion providerSuggestion = providerEvent.getSuggestion(); + + // For the suggestion's effectiveFromElapsedMillis, use the time embedded in the provider's + // suggestion (which indicates the time when the provider detected the location used to + // establish the time zone). + // + // An alternative would be to use the current time or the providerEvent creation time, but + // this would hinder the ability for the time_zone_detector to judge which suggestions are + // based on newer information when comparing suggestions between different sources. + long effectiveFromElapsedMillis = providerSuggestion.getElapsedRealtimeMillis(); + GeolocationTimeZoneSuggestion geoSuggestion = + GeolocationTimeZoneSuggestion.createCertainSuggestion( + effectiveFromElapsedMillis, providerSuggestion.getTimeZoneIds()); + + String debugInfo = "Event received provider=" + provider + + ", providerEvent=" + providerEvent + + ", suggestionCreationTime=" + mEnvironment.elapsedRealtimeMillis(); + geoSuggestion.addDebugInfo(debugInfo); + makeSuggestion(geoSuggestion); + } + + @Override + public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) { + synchronized (mSharedLock) { + ipw.println("LocationTimeZoneProviderController:"); + + ipw.increaseIndent(); // level 1 + ipw.println("mCurrentUserConfiguration=" + mCurrentUserConfiguration); + ipw.println("providerInitializationTimeout=" + + mEnvironment.getProviderInitializationTimeout()); + ipw.println("providerInitializationTimeoutFuzz=" + + mEnvironment.getProviderInitializationTimeoutFuzz()); + ipw.println("uncertaintyDelay=" + mEnvironment.getUncertaintyDelay()); + ipw.println("mLastSuggestion=" + mLastSuggestion); + + ipw.println("Primary Provider:"); + ipw.increaseIndent(); // level 2 + mPrimaryProvider.dump(ipw, args); + ipw.decreaseIndent(); // level 2 + + ipw.println("Secondary Provider:"); + ipw.increaseIndent(); // level 2 + mSecondaryProvider.dump(ipw, args); + ipw.decreaseIndent(); // level 2 + + ipw.decreaseIndent(); // level 1 + } + } + + /** Sends an immediate suggestion, updating mLastSuggestion. */ + @GuardedBy("mSharedLock") + private void makeSuggestion(@NonNull GeolocationTimeZoneSuggestion suggestion) { + debugLog("makeSuggestion: suggestion=" + suggestion); + mCallback.suggest(suggestion); + mLastSuggestion = suggestion; + } + + /** Clears the uncertainty timeout. */ + @GuardedBy("mSharedLock") + private void cancelUncertaintyTimeout() { + mUncertaintyTimeoutQueue.cancel(); + } + + /** + * Called when a provider has become "uncertain" about the time zone. + * + * <p>A provider is expected to report its uncertainty as soon as it becomes uncertain, as + * this enables the most flexibility for the controller to start other providers when there are + * multiple ones available. The controller is therefore responsible for deciding when to make a + * "uncertain" suggestion to the downstream time zone detector. + * + * <p>This method schedules an "uncertainty" timeout (if one isn't already scheduled) to be + * triggered later if nothing else preempts it. It can be preempted if the provider becomes + * certain (or does anything else that calls {@link + * #makeSuggestion(GeolocationTimeZoneSuggestion)}) within {@link + * Environment#getUncertaintyDelay()}. Preemption causes the scheduled + * "uncertainty" timeout to be cancelled. If the provider repeatedly sends uncertainty events + * within the uncertainty delay period, those events are effectively ignored (i.e. the timeout + * is not reset each time). + */ + @GuardedBy("mSharedLock") + void handleProviderUncertainty( + @NonNull LocationTimeZoneProvider provider, + @ElapsedRealtimeLong long uncertaintyStartedElapsedMillis, + @NonNull String reason) { + Objects.requireNonNull(provider); + + // Start the uncertainty timeout if needed to ensure the controller will eventually make an + // uncertain suggestion if no success event arrives in time to counteract it. + if (!mUncertaintyTimeoutQueue.hasQueued()) { + debugLog("Starting uncertainty timeout: reason=" + reason); + + Duration uncertaintyDelay = mEnvironment.getUncertaintyDelay(); + mUncertaintyTimeoutQueue.runDelayed( + () -> onProviderUncertaintyTimeout( + provider, uncertaintyStartedElapsedMillis, uncertaintyDelay), + uncertaintyDelay.toMillis()); + } + + if (provider == mPrimaryProvider) { + // (Try to) start the secondary. It could already be started, or enabling might not + // succeed if the provider has previously reported it is perm failed. The uncertainty + // timeout (set above) is used to ensure that an uncertain suggestion will be made if + // the secondary cannot generate a success event in time. + tryStartProvider(mSecondaryProvider, mCurrentUserConfiguration); + } + } + + private void onProviderUncertaintyTimeout( + @NonNull LocationTimeZoneProvider provider, + @ElapsedRealtimeLong long uncertaintyStartedElapsedMillis, + @NonNull Duration uncertaintyDelay) { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + long afterUncertaintyTimeoutElapsedMillis = mEnvironment.elapsedRealtimeMillis(); + + // For the effectiveFromElapsedMillis suggestion property, use the + // uncertaintyStartedElapsedMillis. This is the time when the provider first reported + // uncertainty, i.e. before the uncertainty timeout. + // + // afterUncertaintyTimeoutElapsedMillis could be used instead, which is the time when + // the location_time_zone_manager finally confirms that the time zone was uncertain, + // but the suggestion property allows the information to be back-dated, which should + // help when comparing suggestions from different sources. + GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( + uncertaintyStartedElapsedMillis, + "Uncertainty timeout triggered for " + provider.getName() + ":" + + " primary=" + mPrimaryProvider + + ", secondary=" + mSecondaryProvider + + ", uncertaintyStarted=" + + Duration.ofMillis(uncertaintyStartedElapsedMillis) + + ", afterUncertaintyTimeout=" + + Duration.ofMillis(afterUncertaintyTimeoutElapsedMillis) + + ", uncertaintyDelay=" + uncertaintyDelay + ); + makeSuggestion(suggestion); + } + } + + @NonNull + private static GeolocationTimeZoneSuggestion createUncertainSuggestion( + @ElapsedRealtimeLong long effectiveFromElapsedMillis, + @NonNull String reason) { + GeolocationTimeZoneSuggestion suggestion = + GeolocationTimeZoneSuggestion.createUncertainSuggestion( + effectiveFromElapsedMillis); + suggestion.addDebugInfo(reason); + return suggestion; + } + + /** + * Clears recorded provider state changes (for use during tests). + */ + void clearRecordedProviderStates() { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + mPrimaryProvider.clearRecordedStates(); + mSecondaryProvider.clearRecordedStates(); + } + } + + /** + * Returns a snapshot of the current controller state for tests. + */ + @NonNull + LocationTimeZoneManagerServiceState getStateForTests() { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + LocationTimeZoneManagerServiceState.Builder builder = + new LocationTimeZoneManagerServiceState.Builder(); + if (mLastSuggestion != null) { + builder.setLastSuggestion(mLastSuggestion); + } + builder.setPrimaryProviderStateChanges(mPrimaryProvider.getRecordedStates()) + .setSecondaryProviderStateChanges(mSecondaryProvider.getRecordedStates()); + return builder.build(); + } + } /** * Used by {@link LocationTimeZoneProviderController} to obtain information from the surrounding diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerCallbackImpl.java index 46eaad075b54..0c751aaa62c7 100644 --- a/services/core/java/com/android/server/timezonedetector/location/ControllerCallbackImpl.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerCallbackImpl.java @@ -24,11 +24,12 @@ import com.android.server.timezonedetector.TimeZoneDetectorInternal; /** * The real implementation of {@link LocationTimeZoneProviderController.Callback} used by - * {@link ControllerImpl} to interact with other server components. + * {@link LocationTimeZoneProviderController} to interact with other server components. */ -class ControllerCallbackImpl extends LocationTimeZoneProviderController.Callback { +class LocationTimeZoneProviderControllerCallbackImpl + extends LocationTimeZoneProviderController.Callback { - ControllerCallbackImpl(@NonNull ThreadingDomain threadingDomain) { + LocationTimeZoneProviderControllerCallbackImpl(@NonNull ThreadingDomain threadingDomain) { super(threadingDomain); } diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerEnvironmentImpl.java index 33cdc5f66def..e7d16c85b1c6 100644 --- a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerEnvironmentImpl.java @@ -29,14 +29,15 @@ import java.util.Objects; /** * The real implementation of {@link LocationTimeZoneProviderController.Environment} used by - * {@link ControllerImpl} to interact with other server components. + * {@link LocationTimeZoneProviderController} to interact with other server components. */ -class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment { +class LocationTimeZoneProviderControllerEnvironmentImpl + extends LocationTimeZoneProviderController.Environment { @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; @NonNull private final ConfigurationChangeListener mConfigurationInternalChangeListener; - ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain, + LocationTimeZoneProviderControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain, @NonNull ServiceConfigAccessor serviceConfigAccessor, @NonNull LocationTimeZoneProviderController controller) { super(threadingDomain); diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java index 463ac5281eb4..20c25a04715b 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java @@ -57,9 +57,9 @@ import java.util.Collections; import java.util.List; import java.util.Objects; -/** Tests for {@link ControllerImpl}. */ +/** Tests for {@link LocationTimeZoneProviderController}. */ @Presubmit -public class ControllerImplTest { +public class LocationTimeZoneProviderControllerTest { private static final long ARBITRARY_TIME_MILLIS = 12345L; @@ -95,10 +95,11 @@ public class ControllerImplTest { @Test public void initializationFailure_primary() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); Duration expectedInitTimeout = testEnvironment.getProviderInitializationTimeout() .plus(testEnvironment.getProviderInitializationTimeoutFuzz()); @@ -106,7 +107,7 @@ public class ControllerImplTest { // Initialize. After initialization the providers must be initialized and one should be // started. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertInitialized(); mTestSecondaryLocationTimeZoneProvider.assertInitialized(); @@ -116,15 +117,16 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void initializationFailure_secondary() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); Duration expectedInitTimeout = testEnvironment.getProviderInitializationTimeout() .plus(testEnvironment.getProviderInitializationTimeoutFuzz()); @@ -132,7 +134,7 @@ public class ControllerImplTest { // Initialize. After initialization the providers must be initialized and one should be // started. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertInitialized(); mTestSecondaryLocationTimeZoneProvider.assertInitialized(); @@ -142,22 +144,23 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout); mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void initializationFailure_both() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestPrimaryLocationTimeZoneProvider.setFailDuringInitialization(true); mTestSecondaryLocationTimeZoneProvider.setFailDuringInitialization(true); // Initialize. After initialization the providers must be initialized and one should be // started. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertInitialized(); mTestSecondaryLocationTimeZoneProvider.assertInitialized(); @@ -165,21 +168,22 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); mTestCallback.assertUncertainSuggestionMadeAndCommit(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void initialState_started() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); Duration expectedInitTimeout = testEnvironment.getProviderInitializationTimeout() .plus(testEnvironment.getProviderInitializationTimeoutFuzz()); // Initialize. After initialization the providers must be initialized and one should be // started. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertInitialized(); mTestSecondaryLocationTimeZoneProvider.assertInitialized(); @@ -189,19 +193,20 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void initialState_disabled() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_DISABLED); // Initialize. After initialization the providers must be initialized but neither should be // started. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertInitialized(); mTestSecondaryLocationTimeZoneProvider.assertInitialized(); @@ -209,24 +214,25 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void enabled_uncertaintySuggestionSentIfNoEventReceived() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate time passing with no provider event being received from the primary. mTestThreadingDomain.executeNext(); @@ -238,7 +244,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); // Simulate time passing with no provider event being received from either the primary or // secondary. @@ -251,7 +257,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); // Finally, the uncertainty timeout should cause the controller to make an uncertain // suggestion. @@ -262,24 +268,25 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertUncertainSuggestionMadeAndCommit(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void enabled_eventReceivedBeforeInitializationTimeout() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate a location event being received from the primary provider. This should cause a // suggestion to be made. @@ -291,24 +298,25 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void enabled_eventReceivedFromPrimaryAfterInitializationTimeout() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate time passing with no provider event being received from the primary. mTestThreadingDomain.executeNext(); @@ -318,7 +326,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); // Simulate a location event being received from the primary provider. This should cause a // suggestion to be made and the secondary to be shut down. @@ -330,24 +338,25 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void enabled_eventReceivedFromSecondaryAfterInitializationTimeout() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate time passing with no provider event being received from the primary. mTestThreadingDomain.executeNext(); @@ -357,7 +366,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); // Simulate a location event being received from the secondary provider. This should cause a // suggestion to be made. @@ -370,24 +379,25 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void enabled_repeatedPrimaryCertainty() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate a location event being received from the primary provider. This should cause a // suggestion to be made. @@ -399,7 +409,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // A second, identical event should not cause another suggestion. mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -409,7 +419,7 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // And a third, different event should cause another suggestion. mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -420,24 +430,25 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void enabled_repeatedSecondaryCertainty() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate time passing with no provider event being received from the primary. mTestThreadingDomain.executeNext(); @@ -447,7 +458,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); // Simulate a location event being received from the secondary provider. This should cause a // suggestion to be made. @@ -460,7 +471,7 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // A second, identical event should not cause another suggestion. mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -471,7 +482,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // And a third, different event should cause another suggestion. mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -483,24 +494,25 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void enabled_uncertaintyTriggersASuggestionAfterUncertaintyTimeout() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate a location event being received from the primary provider. This should cause a // suggestion to be made and ensure the primary is considered initialized. @@ -512,7 +524,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate an uncertain event being received from the primary provider. This should not // cause a suggestion to be made straight away, but the uncertainty timeout should be @@ -525,7 +537,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); // Simulate a location event being received from the secondary provider. This should cause a // suggestion to be made, cancel the uncertainty timeout and ensure the secondary is @@ -539,7 +551,7 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate an uncertain event being received from the secondary provider. This should not // cause a suggestion to be made straight away, but the uncertainty timeout should be @@ -552,7 +564,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); // Simulate time passing. This means the uncertainty timeout should fire and the uncertain // suggestion should be made. @@ -564,24 +576,25 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertUncertainSuggestionMadeFromEventAndCommit( USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void enabled_briefUncertaintyTriggersNoSuggestion() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate a location event being received from the primary provider. This should cause a // suggestion to be made. @@ -593,7 +606,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Uncertainty should not cause a suggestion to be made straight away, but the uncertainty // timeout should be started and the secondary should be started. @@ -605,7 +618,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); // And a success event from the primary provider should cause the controller to make another // suggestion, the uncertainty timeout should be cancelled and the secondary should be @@ -618,23 +631,24 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void configChanges_enableAndDisableWithNoPreviousSuggestion() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_DISABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Now signal a config change so that geo detection is enabled. testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED); @@ -643,7 +657,7 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Now signal a config change so that geo detection is disabled. testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED); @@ -651,23 +665,24 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void configChanges_enableAndDisableWithPreviousSuggestion() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_DISABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Now signal a config change so that geo detection is enabled. testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED); @@ -676,7 +691,7 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate a success event being received from the primary provider. mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -687,7 +702,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Now signal a config change so that geo detection is disabled. // Because there had been a previous suggestion, the controller should withdraw it @@ -698,24 +713,25 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertUncertainSuggestionMadeAndCommit(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void configChanges_userSwitch_enabledToEnabled() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate the primary provider suggesting a time zone. mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -728,7 +744,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate the user change (but geo detection still enabled). testEnvironment.simulateConfigChange(USER2_CONFIG_GEO_DETECTION_ENABLED); @@ -744,24 +760,25 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfig( PROVIDER_STATE_STARTED_INITIALIZING, USER2_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void primaryPermFailure_secondaryEventsReceived() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate a failure location event being received from the primary provider. This should // cause the secondary to be started. @@ -772,7 +789,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate uncertainty from the secondary. mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -782,7 +799,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); // And a success event from the secondary provider should cause the controller to make // another suggestion, the uncertainty timeout should be cancelled. @@ -794,7 +811,7 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate uncertainty from the secondary. mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -804,24 +821,25 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); } @Test public void primaryPermFailure_disableAndEnable() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate a failure location event being received from the primary provider. This should // cause the secondary to be started. @@ -832,7 +850,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Now signal a config change so that geo detection is disabled. testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED); @@ -840,7 +858,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Now signal a config change so that geo detection is enabled. testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED); @@ -849,24 +867,25 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void secondaryPermFailure_primaryEventsReceived() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate an uncertain event from the primary. This will start the secondary, which will // give this test the opportunity to simulate its failure. Then it will be possible to @@ -879,7 +898,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); // Simulate failure event from the secondary. This should just affect the secondary's state. mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -889,7 +908,7 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); // And a success event from the primary provider should cause the controller to make // a suggestion, the uncertainty timeout should be cancelled. @@ -901,7 +920,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate uncertainty from the primary. The secondary cannot be started. mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -911,24 +930,25 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); } @Test public void secondaryPermFailure_disableAndEnable() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate an uncertain event from the primary. This will start the secondary, which will // give this test the opportunity to simulate its failure. Then it will be possible to @@ -941,7 +961,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); // Simulate failure event from the secondary. This should just affect the secondary's state. mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -951,7 +971,7 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + assertUncertaintyTimeoutSet(testEnvironment, controller); // Now signal a config change so that geo detection is disabled. testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED); @@ -959,7 +979,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Now signal a config change so that geo detection is enabled. Only the primary can be // started. @@ -969,24 +989,25 @@ public class ControllerImplTest { PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void bothPermFailure_disableAndEnable() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate a failure event from the primary. This will start the secondary. mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -996,7 +1017,7 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate failure event from the secondary. mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -1005,28 +1026,29 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); mTestCallback.assertUncertainSuggestionMadeAndCommit(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } @Test public void stateRecording() { // The test provider enables state recording by default. - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial states. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); { - LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests(); + LocationTimeZoneManagerServiceState state = controller.getStateForTests(); assertNull(state.getLastSuggestion()); assertProviderStates(state.getPrimaryProviderStates(), PROVIDER_STATE_STOPPED, PROVIDER_STATE_STARTED_INITIALIZING); assertProviderStates(state.getSecondaryProviderStates(), PROVIDER_STATE_STOPPED); } - controllerImpl.clearRecordedProviderStates(); + controller.clearRecordedProviderStates(); // Simulate some provider behavior that will show up in the state recording. @@ -1035,21 +1057,21 @@ public class ControllerImplTest { USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT); { - LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests(); + LocationTimeZoneManagerServiceState state = controller.getStateForTests(); assertNull(state.getLastSuggestion()); assertProviderStates( state.getPrimaryProviderStates(), PROVIDER_STATE_STARTED_UNCERTAIN); assertProviderStates( state.getSecondaryProviderStates(), PROVIDER_STATE_STARTED_INITIALIZING); } - controllerImpl.clearRecordedProviderStates(); + controller.clearRecordedProviderStates(); // Simulate a certain event from the secondary. mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); { - LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests(); + LocationTimeZoneManagerServiceState state = controller.getStateForTests(); assertEquals(USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds(), state.getLastSuggestion().getZoneIds()); assertProviderStates(state.getPrimaryProviderStates()); @@ -1057,9 +1079,9 @@ public class ControllerImplTest { state.getSecondaryProviderStates(), PROVIDER_STATE_STARTED_CERTAIN); } - controllerImpl.clearRecordedProviderStates(); + controller.clearRecordedProviderStates(); { - LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests(); + LocationTimeZoneManagerServiceState state = controller.getStateForTests(); assertEquals(USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds(), state.getLastSuggestion().getZoneIds()); assertProviderStates(state.getPrimaryProviderStates()); @@ -1078,19 +1100,20 @@ public class ControllerImplTest { @Test public void destroy() { - ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController( + mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, + mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( - mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED); // Initialize and check initial state. - controllerImpl.initialize(testEnvironment, mTestCallback); + controller.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertNoSuggestionMade(); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Simulate the primary provider suggesting a time zone. mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( @@ -1103,10 +1126,10 @@ public class ControllerImplTest { mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit(); mTestCallback.assertCertainSuggestionMadeFromEventAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); // Trigger destroy(). - controllerImpl.destroy(); + controller.destroy(); // Confirm that the previous suggestion was overridden. mTestCallback.assertUncertainSuggestionMadeAndCommit(); @@ -1115,7 +1138,7 @@ public class ControllerImplTest { PROVIDER_STATE_STOPPED, PROVIDER_STATE_DESTROYED); mTestSecondaryLocationTimeZoneProvider.assertStateChangesAndCommit( PROVIDER_STATE_DESTROYED); - assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + assertFalse(controller.isUncertaintyTimeoutSet()); } private static void assertUncertaintyTimeoutSet( |