Remove base class / rename Controller classes
Refactoring change before an upcoming behavior change commit. The LocationTimeZoneProviderController class isn't pulling its weight, so the ControllerImpl has been merged into it. The support classes ControllerCallbackImpl and ControllerEnvironmentImpl classes have been renamed to be prefixed by LocationTimeZone for consistency. Test: build / treehugger Bug: 200279201 Change-Id: I9757edfe878d24546b2e82e5e761aea47e6a0679
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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 protected final ThreadingDomain mThreadingDomain;
|
||||
@NonNull protected final Object mSharedLock;
|
||||
@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;
|
||||
|
||||
LocationTimeZoneProviderController(@NonNull ThreadingDomain threadingDomain) {
|
||||
@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;
|
||||
|
||||
|
||||
/** 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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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(
|
||||
Reference in New Issue
Block a user