Merge "Remove base class / rename Controller classes"
This commit is contained in:
committed by
Android (Google) Code Review
commit
35fd8c4a4a
@@ -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