diff --git a/location/java/android/location/timezone/LocationTimeZoneEvent.java b/location/java/android/location/timezone/LocationTimeZoneEvent.java index 55bc507964e6e..d3fd5c3ac061c 100644 --- a/location/java/android/location/timezone/LocationTimeZoneEvent.java +++ b/location/java/android/location/timezone/LocationTimeZoneEvent.java @@ -23,6 +23,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; +import com.android.internal.util.Preconditions; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -37,7 +39,7 @@ public final class LocationTimeZoneEvent implements Parcelable { @IntDef({ EVENT_TYPE_UNKNOWN, EVENT_TYPE_PERMANENT_FAILURE, EVENT_TYPE_SUCCESS, EVENT_TYPE_UNCERTAIN }) - @interface EventType {} + public @interface EventType {} /** Uninitialized value for {@link #mEventType} - must not be used for real events. */ private static final int EVENT_TYPE_UNKNOWN = 0; @@ -49,8 +51,8 @@ public final class LocationTimeZoneEvent implements Parcelable { public static final int EVENT_TYPE_PERMANENT_FAILURE = 1; /** - * Indicates a successful geolocation time zone detection event. {@link #mTimeZoneIds} will be - * non-null but can legitimately be empty, e.g. for disputed areas, oceans. + * Indicates a successful geolocation time zone detection event. {@link #getTimeZoneIds()} will + * be non-null but can legitimately be empty, e.g. for disputed areas, oceans. */ public static final int EVENT_TYPE_SUCCESS = 2; @@ -81,10 +83,7 @@ public final class LocationTimeZoneEvent implements Parcelable { mTimeZoneIds = immutableList(timeZoneIds); boolean emptyTimeZoneIdListExpected = eventType != EVENT_TYPE_SUCCESS; - if (emptyTimeZoneIdListExpected && !timeZoneIds.isEmpty()) { - throw new IllegalStateException( - "timeZoneIds must only have values when eventType is success"); - } + Preconditions.checkState(!emptyTimeZoneIdListExpected || timeZoneIds.isEmpty()); mElapsedRealtimeNanos = elapsedRealtimeNanos; } @@ -102,9 +101,7 @@ public final class LocationTimeZoneEvent implements Parcelable { * *
This value can be reliably compared to {@link
* android.os.SystemClock#elapsedRealtimeNanos}, to calculate the age of a fix and to compare
- * {@link LocationTimeZoneEvent} fixes. This is reliable because elapsed real-time is guaranteed
- * monotonic for each system boot and continues to increment even when the system is in deep
- * sleep.
+ * {@link LocationTimeZoneEvent} instances.
*
* @return elapsed real-time of fix, in nanoseconds since system boot.
*/
diff --git a/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.java b/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.java
index 2a37ef882e619..5c9d2908f5dff 100644
--- a/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.java
+++ b/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.java
@@ -30,7 +30,9 @@ import java.util.Objects;
public final class LocationTimeZoneProviderRequest implements Parcelable {
public static final LocationTimeZoneProviderRequest EMPTY_REQUEST =
- new LocationTimeZoneProviderRequest(false);
+ new LocationTimeZoneProviderRequest(
+ false /* reportLocationTimeZone */,
+ 0 /* initializationTimeoutMillis */);
public static final Creator Provider implementations are enabled / disabled via a call to {@link
+ * #onSetRequest(LocationTimeZoneProviderRequestUnbundled)}.
+ *
+ * Once enabled, providers are expected to detect the time zone if possible, and report the
+ * result via {@link #reportLocationTimeZoneEvent(LocationTimeZoneEventUnbundled)} with a type of
+ * either {@link LocationTimeZoneEventUnbundled#EVENT_TYPE_UNCERTAIN} or {@link
+ * LocationTimeZoneEventUnbundled#EVENT_TYPE_SUCCESS}. Providers may also report that they have
+ * permanently failed by sending an event of type {@link
+ * LocationTimeZoneEventUnbundled#EVENT_TYPE_PERMANENT_FAILURE}. See the javadocs for each event
+ * type for details.
+ *
+ * Providers are expected to issue their first event within {@link
+ * LocationTimeZoneProviderRequest#getInitializationTimeoutMillis()}.
+ *
+ * Once disabled or have failed, providers are required to stop producing events.
+ *
+ * Threading:
+ *
+ * Calls to {@link #reportLocationTimeZoneEvent(LocationTimeZoneEventUnbundled)} can be made on
+ * on any thread, but may be processed asynchronously by the system server. Similarly, calls to
+ * {@link #onSetRequest(LocationTimeZoneProviderRequestUnbundled)} may occur on any thread.
*
* IMPORTANT: This class is effectively a public API for unbundled applications, and must remain
* API stable.
+ *
+ * @hide
*/
public abstract class LocationTimeZoneProviderBase {
@@ -64,11 +85,11 @@ public abstract class LocationTimeZoneProviderBase {
/**
* Reports a new location time zone event from this provider.
*/
- public void reportLocationTimeZoneEvent(LocationTimeZoneEvent locationTimeZoneEvent) {
+ protected void reportLocationTimeZoneEvent(LocationTimeZoneEventUnbundled event) {
ILocationTimeZoneProviderManager manager = mManager;
if (manager != null) {
try {
- manager.onLocationTimeZoneEvent(locationTimeZoneEvent);
+ manager.onLocationTimeZoneEvent(event.getInternalLocationTimeZoneEvent());
} catch (RemoteException | RuntimeException e) {
Log.w(mTag, e);
}
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
index e898bbf3ecc0c..ab50dc3b00fce 100644
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
@@ -34,15 +34,30 @@ public final class LocationTimeZoneProviderRequestUnbundled {
private final LocationTimeZoneProviderRequest mRequest;
+ /** @hide */
public LocationTimeZoneProviderRequestUnbundled(
@NonNull LocationTimeZoneProviderRequest request) {
mRequest = Objects.requireNonNull(request);
}
+ /**
+ * Returns {@code true} if the provider should report events related to the device's current
+ * time zone, {@code false} otherwise.
+ */
public boolean getReportLocationTimeZone() {
return mRequest.getReportLocationTimeZone();
}
+ /**
+ * Returns the maximum time that the provider is allowed to initialize before it is expected to
+ * send an event of any sort. Only valid when {@link #getReportLocationTimeZone()} is {@code
+ * true}. Failure to send an event in this time (with some fuzz) may be interpreted as if the
+ * provider is uncertain of the time zone, and/or it could lead to the provider being disabled.
+ */
+ public long getInitializationTimeoutMillis() {
+ return mRequest.getInitializationTimeoutMillis();
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
index 92dabe337d87c..e28d73e8e0b98 100644
--- a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
@@ -29,6 +29,7 @@ import android.util.Slog;
import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
+import java.time.Duration;
import java.util.Objects;
/**
@@ -142,14 +143,13 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
}
@Override
- void onEnable() {
+ void onEnable(@NonNull Duration initializationTimeout) {
// Set a request on the proxy - it will be sent immediately if the service is bound,
// or will be sent as soon as the service becomes bound.
- // TODO(b/152744911): Decide whether to send a timeout so the provider knows how long
- // it has to generate the first event before it could be bypassed.
LocationTimeZoneProviderRequest request =
new LocationTimeZoneProviderRequest.Builder()
.setReportLocationTimeZone(true)
+ .setInitializationTimeoutMillis(initializationTimeout.toMillis())
.build();
mProxy.setRequest(request);
}
diff --git a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
index 2e2481c30f222..b1e3306681944 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
+++ b/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
@@ -22,6 +22,7 @@ import com.android.server.LocalServices;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
+import java.time.Duration;
import java.util.Objects;
/**
@@ -30,6 +31,10 @@ import java.util.Objects;
*/
class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {
+ private static final Duration PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
+ private static final Duration PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = Duration.ofMinutes(1);
+ private static final Duration PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
+
@NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
@NonNull private final LocationTimeZoneProviderController mController;
@@ -45,7 +50,26 @@ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Envir
}
@Override
+ @NonNull
ConfigurationInternal getCurrentUserConfigurationInternal() {
return mTimeZoneDetectorInternal.getCurrentUserConfigurationInternal();
}
+
+ @Override
+ @NonNull
+ Duration getProviderInitializationTimeout() {
+ return PROVIDER_INITIALIZATION_TIMEOUT;
+ }
+
+ @Override
+ @NonNull
+ Duration getProviderInitializationTimeoutFuzz() {
+ return PROVIDER_INITIALIZATION_TIMEOUT_FUZZ;
+ }
+
+ @Override
+ @NonNull
+ Duration getUncertaintyDelay() {
+ return PROVIDER_UNCERTAINTY_DELAY;
+ }
}
diff --git a/services/core/java/com/android/server/location/timezone/ControllerImpl.java b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
index e31cfc4e8b408..bedaedacf0e3d 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerImpl.java
+++ b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
@@ -33,7 +33,6 @@ import android.location.timezone.LocationTimeZoneEvent;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
@@ -50,9 +49,6 @@ import java.util.Objects;
*/
class ControllerImpl extends LocationTimeZoneProviderController {
- @VisibleForTesting
- static final Duration UNCERTAINTY_DELAY = Duration.ofMinutes(5);
-
@NonNull private final LocationTimeZoneProvider mProvider;
@NonNull private final SingleRunnableQueue mDelayedSuggestionQueue;
@@ -140,7 +136,8 @@ class ControllerImpl extends LocationTimeZoneProviderController {
switch (providerState.stateEnum) {
case PROVIDER_STATE_DISABLED: {
debugLog("Enabling " + mProvider);
- mProvider.enable(configuration);
+ mProvider.enable(
+ configuration, mEnvironment.getProviderInitializationTimeout());
break;
}
case PROVIDER_STATE_ENABLED: {
@@ -183,13 +180,16 @@ class ControllerImpl extends LocationTimeZoneProviderController {
if (isProviderEnabled) {
if (!providerWasEnabled) {
// When a provider has first been enabled, we allow it some time for it to
- // initialize.
+ // initialize before sending its first event.
+ Duration initializationTimeout = mEnvironment.getProviderInitializationTimeout()
+ .plus(mEnvironment.getProviderInitializationTimeoutFuzz());
// This sets up an empty suggestion to trigger if no explicit "certain" or
- // "uncertain" suggestion preempts it within UNCERTAINTY_DELAY. If, for some reason,
- // the provider does provide any events then this scheduled suggestion will ensure
- // the controller makes at least an uncertain suggestion.
- suggestDelayed(createEmptySuggestion(
- "No event received in delay=" + UNCERTAINTY_DELAY), UNCERTAINTY_DELAY);
+ // "uncertain" suggestion preempts it within initializationTimeout. If, for some
+ // reason, the provider does not produce any events then this scheduled suggestion
+ // will ensure the controller makes at least an "uncertain" suggestion.
+ suggestDelayed(createEmptySuggestion("No event received from provider in"
+ + " initializationTimeout=" + initializationTimeout),
+ initializationTimeout);
}
} else {
// Clear any queued suggestions.
@@ -199,7 +199,8 @@ class ControllerImpl extends LocationTimeZoneProviderController {
// made, then a new "uncertain" suggestion must be made to indicate the provider no
// longer has an opinion and will not be sending updates.
if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
- suggestImmediate(createEmptySuggestion(""));
+ suggestImmediate(createEmptySuggestion(
+ "Provider disabled, clearing previous suggestion"));
}
}
}
@@ -309,21 +310,23 @@ class ControllerImpl extends LocationTimeZoneProviderController {
*
* Providers are expected to report their uncertainty as soon as they become uncertain, as
* this enables the most flexibility for the controller to enable other providers when there are
- * multiple ones. The controller is therefore responsible for deciding when to make a
+ * multiple ones available. The controller is therefore responsible for deciding when to make a
* "uncertain" suggestion.
*
* This method schedules an "uncertain" suggestion (if one isn't already scheduled) to be
* made later if nothing else preempts it. It can be preempted if the provider becomes certain
* (or does anything else that calls {@link #suggestImmediate(GeolocationTimeZoneSuggestion)})
- * within UNCERTAINTY_DELAY. Preemption causes the scheduled "uncertain" event to be cancelled.
- * If the provider repeatedly sends uncertainty events within UNCERTAINTY_DELAY, those events
- * are effectively ignored (i.e. the timer is not reset each time).
+ * within {@link Environment#getUncertaintyDelay()}. Preemption causes the scheduled
+ * "uncertain" event to be cancelled. If the provider repeatedly sends uncertainty events within
+ * the uncertainty delay period, those events are effectively ignored (i.e. the timer is not
+ * reset each time).
*/
private void scheduleUncertainSuggestionIfNeeded(@Nullable LocationTimeZoneEvent event) {
if (mPendingSuggestion == null || mPendingSuggestion.getZoneIds() != null) {
GeolocationTimeZoneSuggestion suggestion = createEmptySuggestion(
"provider=" + mProvider + " became uncertain, event=" + event);
- suggestDelayed(suggestion, UNCERTAINTY_DELAY);
+ // Only send the empty suggestion after the uncertainty delay.
+ suggestDelayed(suggestion, mEnvironment.getUncertaintyDelay());
}
}
@@ -334,6 +337,11 @@ class ControllerImpl extends 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("mPendingSuggestion=" + mPendingSuggestion);
ipw.println("mLastSuggestion=" + mLastSuggestion);
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
index 3743779b20c91..abfa580b5c980 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
@@ -37,6 +37,7 @@ import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.Dumpable;
import com.android.server.timezonedetector.ReferenceWithHistory;
+import java.time.Duration;
import java.util.Objects;
/**
@@ -368,7 +369,8 @@ abstract class LocationTimeZoneProvider implements Dumpable {
* #getCurrentState()} is at {@link ProviderState#PROVIDER_STATE_DISABLED}. This method must be
* called using the handler thread from the {@link ThreadingDomain}.
*/
- final void enable(@NonNull ConfigurationInternal currentUserConfiguration) {
+ final void enable(@NonNull ConfigurationInternal currentUserConfiguration,
+ @NonNull Duration initializationTimeout) {
mThreadingDomain.assertCurrentThread();
synchronized (mSharedLock) {
@@ -378,14 +380,14 @@ abstract class LocationTimeZoneProvider implements Dumpable {
ProviderState newState = currentState.newState(
PROVIDER_STATE_ENABLED, null, currentUserConfiguration, "enable() called");
setCurrentState(newState, false);
- onEnable();
+ onEnable(initializationTimeout);
}
}
/**
* Implemented by subclasses to do work during {@link #enable}.
*/
- abstract void onEnable();
+ abstract void onEnable(@NonNull Duration initializationTimeout);
/**
* Disables the provider. It is an error* to call this method except when the {@link
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
index 2f75c43594df6..88f0f00015c6f 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
@@ -24,6 +24,7 @@ import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.Dumpable;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import java.time.Duration;
import java.util.Objects;
/**
@@ -100,6 +101,24 @@ abstract class LocationTimeZoneProviderController implements Dumpable {
/** Returns the {@link ConfigurationInternal} for the current user of the device. */
abstract ConfigurationInternal getCurrentUserConfigurationInternal();
+
+ /**
+ * Returns the value passed to LocationTimeZoneProviders informing them of how long they
+ * have to return their first time zone suggestion.
+ */
+ abstract Duration getProviderInitializationTimeout();
+
+ /**
+ * Returns the extra time granted on top of {@link #getProviderInitializationTimeout()} to
+ * allow for slop like communication delays.
+ */
+ abstract Duration getProviderInitializationTimeoutFuzz();
+
+ /**
+ * Returns the delay allowed after receiving uncertainty from a provider before it should be
+ * passed on.
+ */
+ abstract Duration getUncertaintyDelay();
}
/**
diff --git a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
index 79e2b975089d1..53afaf06f2983 100644
--- a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
@@ -23,6 +23,8 @@ import android.annotation.Nullable;
import android.util.IndentingPrintWriter;
import android.util.Slog;
+import java.time.Duration;
+
/**
* A {@link LocationTimeZoneProvider} that provides minimal responses needed for the {@link
* LocationTimeZoneProviderController} to operate correctly when there is no "real" provider
@@ -55,7 +57,7 @@ class NullLocationTimeZoneProvider extends LocationTimeZoneProvider {
}
@Override
- void onEnable() {
+ void onEnable(@NonNull Duration initializationTimeout) {
// Report a failure (asynchronously using the mThreadingDomain thread to avoid recursion).
mThreadingDomain.post(()-> {
// Enter the perm-failed state.
diff --git a/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java b/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java
index 1fa1b8fc06196..75696dac3b44d 100644
--- a/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java
+++ b/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java
@@ -20,16 +20,20 @@ import static android.location.timezone.ParcelableTestSupport.assertRoundTripPar
import org.junit.Test;
+import java.time.Duration;
+
public class LocationTimeZoneProviderRequestTest {
@Test
public void testParcelable() {
LocationTimeZoneProviderRequest.Builder builder =
new LocationTimeZoneProviderRequest.Builder()
- .setReportLocationTimeZone(true);
+ .setReportLocationTimeZone(false);
assertRoundTripParcelable(builder.build());
- builder.setReportLocationTimeZone(false);
+ builder.setReportLocationTimeZone(true)
+ .setInitializationTimeoutMillis(Duration.ofMinutes(5).toMillis());
+
assertRoundTripParcelable(builder.build());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
index dbaad6633e98d..631b8d3cb1ea0 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
@@ -18,7 +18,6 @@ package com.android.server.location.timezone;
import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
-import static com.android.server.location.timezone.ControllerImpl.UNCERTAINTY_DELAY;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED;
import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_DISABLED;
@@ -47,6 +46,7 @@ import com.android.server.timezonedetector.TestState;
import org.junit.Before;
import org.junit.Test;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -92,7 +92,8 @@ public class ControllerImplTest {
mTestLocationTimeZoneProvider.assertInitialized();
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertNextQueueItemIsDelayed(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
}
@@ -121,7 +122,8 @@ public class ControllerImplTest {
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
// Simulate time passing with no event being received.
mTestThreadingDomain.executeNext();
@@ -140,7 +142,8 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Simulate a location event being received by the provider. This should cause a suggestion
@@ -163,7 +166,8 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Simulate a location event being received by the provider. This should cause a suggestion
@@ -203,7 +207,8 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Simulate a location event being received by the provider. This should cause a suggestion
@@ -216,15 +221,16 @@ public class ControllerImplTest {
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
- // Uncertainty should cause
+ // Uncertainty should cause a suggestion to (only) be queued.
mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(testEnvironment.getUncertaintyDelay());
mTestCallback.assertNoSuggestionMade();
- // And a third event should cause yet another suggestion.
+ // And a third event should cause yet another suggestion and for the queued item to be
+ // removed.
mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
@@ -250,7 +256,8 @@ public class ControllerImplTest {
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertNextQueueItemIsDelayed(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Now signal a config change so that geo detection is disabled.
@@ -270,7 +277,8 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Simulate a location event being received by the provider. This should cause a suggestion
@@ -304,7 +312,8 @@ public class ControllerImplTest {
// There should be a runnable scheduled to suggest uncertainty if no event is received.
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Have the provider suggest a time zone.
@@ -328,7 +337,8 @@ public class ControllerImplTest {
int[] expectedStateTransitions = { PROVIDER_STATE_DISABLED, PROVIDER_STATE_ENABLED };
mTestLocationTimeZoneProvider.assertStateChangesAndCommit(expectedStateTransitions);
mTestLocationTimeZoneProvider.assertConfig(USER2_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Simulate no event being received, and time passing.
@@ -351,8 +361,18 @@ public class ControllerImplTest {
return builder.build();
}
+
+ private Duration expectedProviderInitializationTimeout() {
+ return TestEnvironment.PROVIDER_INITIALIZATION_TIMEOUT
+ .plus(TestEnvironment.PROVIDER_INITIALIZATION_TIMEOUT_FUZZ);
+ }
+
private static class TestEnvironment extends LocationTimeZoneProviderController.Environment {
+ static final Duration PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
+ static final Duration PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = Duration.ofMinutes(1);
+ private static final Duration UNCERTAINTY_DELAY = Duration.ofMinutes(3);
+
private final LocationTimeZoneProviderController mController;
private ConfigurationInternal mConfigurationInternal;
@@ -369,6 +389,21 @@ public class ControllerImplTest {
return mConfigurationInternal;
}
+ @Override
+ Duration getProviderInitializationTimeout() {
+ return PROVIDER_INITIALIZATION_TIMEOUT;
+ }
+
+ @Override
+ Duration getProviderInitializationTimeoutFuzz() {
+ return PROVIDER_INITIALIZATION_TIMEOUT_FUZZ;
+ }
+
+ @Override
+ Duration getUncertaintyDelay() {
+ return UNCERTAINTY_DELAY;
+ }
+
void simulateConfigChange(ConfigurationInternal newConfig) {
ConfigurationInternal oldConfig = mConfigurationInternal;
mConfigurationInternal = Objects.requireNonNull(newConfig);
@@ -432,7 +467,7 @@ public class ControllerImplTest {
}
@Override
- void onEnable() {
+ void onEnable(Duration initializationTimeout) {
// Nothing needed for tests.
}
@@ -464,7 +499,7 @@ public class ControllerImplTest {
/**
* Asserts the provider's config matches the expected, and the current state is set
- * accordinly. Commits the latest changes to the state.
+ * accordingly. Commits the latest changes to the state.
*/
void assertIsEnabled(@NonNull ConfigurationInternal expectedConfig) {
assertConfig(expectedConfig);
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java
index 7c882fc1f1543..5542db0b5ae3d 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java
@@ -34,6 +34,8 @@ import com.android.server.timezonedetector.TestState;
import org.junit.Before;
import org.junit.Test;
+import java.time.Duration;
+
/**
* Tests for {@link NullLocationTimeZoneProvider} and, indirectly, the class it extends
* {@link LocationTimeZoneProvider}.
@@ -73,7 +75,8 @@ public class NullLocationTimeZoneProviderTest {
provider.initialize(providerState -> mTestController.onProviderStateChange(providerState));
ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
- provider.enable(config);
+ Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
+ provider.enable(config, arbitraryInitializationTimeout);
// The StubbedProvider should enters enabled state, but immediately schedule a runnable to
// switch to perm failure.
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java b/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
index 70ff22d175136..def919e79731a 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
@@ -105,8 +105,8 @@ class TestThreadingDomain extends ThreadingDomain {
assertTrue(getNextQueueItemDelayMillis() == 0);
}
- void assertNextQueueItemIsDelayed(Duration expectedDelay) {
- assertTrue(getNextQueueItemDelayMillis() == expectedDelay.toMillis());
+ private void assertNextQueueItemIsDelayed(Duration expectedDelay) {
+ assertEquals(getNextQueueItemDelayMillis(), expectedDelay.toMillis());
}
void assertQueueEmpty() {