Adding selective filtering to LTZP events

This commit introduces a time threshold for passing on "equivalent"
events for location time zone providers. When TimeZoneProviderEvent
instances are created, they are assigned a time from the elapsed
realtime clock, which will enable comparison with other types of events
generated by other flows. Events that have a sufficiently different time
will be passed to the system server. The time will be in the order of a
few minutes, the intention being to filter out high frequency events
that would cause the system server to do work unnecessarily, while
keeping a coarse idea of how recently the LTZP reported the state.

This commit adds the time to the TimeZoneProviderEvent and plumbs
through the configuration for the filter threshold from the system
server to the client code. The threshold can be set with server flags,
which are mostly intended to support the test logic.

Changes made at the same time:
1) Fix an issue in TimeZoneProviderRequest.equals() with an incorrect
reference equality check.
2) Rename some long-winded constant names - LOCATION_TIME_ZONE_PROVIDER_
-> LTZP_
3) Change some server flag strings so that "ltzp_" is spelt correctly
(which I do not anticipate using, but does mean that S and >S devices
respond to different flag strings).

Bug: 197624972
Bug: 200710190
Test: atest core/tests/coretests/src/android/service/timezone/
Test: atest services/tests/servicestests/src/com/android/server/timezonedetector/location/
Test: See associated cts/ change
Change-Id: Id682d420be21b9215b92f54bad599517c322fb6a
This commit is contained in:
Neil Fuller
2021-10-13 10:31:36 +01:00
parent a395ffdd2c
commit 135703feac
22 changed files with 362 additions and 112 deletions

View File

@@ -22,6 +22,7 @@ import android.service.timezone.ITimeZoneProviderManager;
* @hide
*/
oneway interface ITimeZoneProvider {
void startUpdates(in ITimeZoneProviderManager manager, in long initializationTimeoutMillis);
void startUpdates(in ITimeZoneProviderManager manager, in long initializationTimeoutMillis,
in long eventFilteringAgeThresholdMillis);
void stopUpdates();
}

View File

@@ -16,6 +16,7 @@
package android.service.timezone;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,6 +27,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.time.Duration;
import java.util.Objects;
/**
@@ -59,11 +61,11 @@ public final class TimeZoneProviderEvent implements Parcelable {
*/
public static final @EventType int EVENT_TYPE_UNCERTAIN = 3;
private static final TimeZoneProviderEvent UNCERTAIN_EVENT =
new TimeZoneProviderEvent(EVENT_TYPE_UNCERTAIN, null, null);
private final @EventType int mType;
@ElapsedRealtimeLong
private final long mCreationElapsedMillis;
@Nullable
private final TimeZoneProviderSuggestion mSuggestion;
@@ -71,28 +73,34 @@ public final class TimeZoneProviderEvent implements Parcelable {
private final String mFailureCause;
private TimeZoneProviderEvent(@EventType int type,
@ElapsedRealtimeLong long creationElapsedMillis,
@Nullable TimeZoneProviderSuggestion suggestion,
@Nullable String failureCause) {
mType = type;
mCreationElapsedMillis = creationElapsedMillis;
mSuggestion = suggestion;
mFailureCause = failureCause;
}
/** Returns a event of type {@link #EVENT_TYPE_SUGGESTION}. */
public static TimeZoneProviderEvent createSuggestionEvent(
@ElapsedRealtimeLong long creationElapsedMillis,
@NonNull TimeZoneProviderSuggestion suggestion) {
return new TimeZoneProviderEvent(EVENT_TYPE_SUGGESTION,
return new TimeZoneProviderEvent(EVENT_TYPE_SUGGESTION, creationElapsedMillis,
Objects.requireNonNull(suggestion), null);
}
/** Returns a event of type {@link #EVENT_TYPE_UNCERTAIN}. */
public static TimeZoneProviderEvent createUncertainEvent() {
return UNCERTAIN_EVENT;
public static TimeZoneProviderEvent createUncertainEvent(
@ElapsedRealtimeLong long creationElapsedMillis) {
return new TimeZoneProviderEvent(EVENT_TYPE_UNCERTAIN, creationElapsedMillis, null, null);
}
/** Returns a event of type {@link #EVENT_TYPE_PERMANENT_FAILURE}. */
public static TimeZoneProviderEvent createPermanentFailureEvent(@NonNull String cause) {
return new TimeZoneProviderEvent(EVENT_TYPE_PERMANENT_FAILURE, null,
public static TimeZoneProviderEvent createPermanentFailureEvent(
@ElapsedRealtimeLong long creationElapsedMillis,
@NonNull String cause) {
return new TimeZoneProviderEvent(EVENT_TYPE_PERMANENT_FAILURE, creationElapsedMillis, null,
Objects.requireNonNull(cause));
}
@@ -103,6 +111,12 @@ public final class TimeZoneProviderEvent implements Parcelable {
return mType;
}
/** Returns the time according to the elapsed realtime clock when the event was created. */
@ElapsedRealtimeLong
public long getCreationElapsedMillis() {
return mCreationElapsedMillis;
}
/**
* Returns the suggestion. Populated when {@link #getType()} is {@link #EVENT_TYPE_SUGGESTION}.
*/
@@ -125,10 +139,12 @@ public final class TimeZoneProviderEvent implements Parcelable {
@Override
public TimeZoneProviderEvent createFromParcel(Parcel in) {
int type = in.readInt();
long creationElapsedMillis = in.readLong();
TimeZoneProviderSuggestion suggestion =
in.readParcelable(getClass().getClassLoader());
String failureCause = in.readString8();
return new TimeZoneProviderEvent(type, suggestion, failureCause);
return new TimeZoneProviderEvent(
type, creationElapsedMillis, suggestion, failureCause);
}
@Override
@@ -145,6 +161,7 @@ public final class TimeZoneProviderEvent implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeInt(mType);
parcel.writeLong(mCreationElapsedMillis);
parcel.writeParcelable(mSuggestion, 0);
parcel.writeString8(mFailureCause);
}
@@ -153,6 +170,7 @@ public final class TimeZoneProviderEvent implements Parcelable {
public String toString() {
return "TimeZoneProviderEvent{"
+ "mType=" + mType
+ ", mCreationElapsedMillis=" + Duration.ofMillis(mCreationElapsedMillis).toString()
+ ", mSuggestion=" + mSuggestion
+ ", mFailureCause=" + mFailureCause
+ '}';
@@ -173,8 +191,7 @@ public final class TimeZoneProviderEvent implements Parcelable {
return false;
}
if (mType == EVENT_TYPE_SUGGESTION) {
// Only check the time zone IDs. The times will be different, but we don't mind.
return mSuggestion.getTimeZoneIds().equals(other.getSuggestion().getTimeZoneIds());
return mSuggestion.isEquivalentTo(other.getSuggestion());
}
return true;
}
@@ -189,12 +206,13 @@ public final class TimeZoneProviderEvent implements Parcelable {
}
TimeZoneProviderEvent that = (TimeZoneProviderEvent) o;
return mType == that.mType
&& mCreationElapsedMillis == that.mCreationElapsedMillis
&& Objects.equals(mSuggestion, that.mSuggestion)
&& Objects.equals(mFailureCause, that.mFailureCause);
}
@Override
public int hashCode() {
return Objects.hash(mType, mSuggestion, mFailureCause);
return Objects.hash(mType, mCreationElapsedMillis, mSuggestion, mFailureCause);
}
}

View File

@@ -26,6 +26,7 @@ import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -178,6 +179,10 @@ public abstract class TimeZoneProviderService extends Service {
@Nullable
private ITimeZoneProviderManager mManager;
/** Set by {@link #mHandler} thread. */
@GuardedBy("mLock")
private long mEventFilteringAgeThresholdMillis;
/**
* The type of the last suggestion sent to the system server. Used to de-dupe suggestions client
* side and avoid calling into the system server unnecessarily. {@code null} means no previous
@@ -206,8 +211,9 @@ public abstract class TimeZoneProviderService extends Service {
if (manager != null) {
try {
TimeZoneProviderEvent thisEvent =
TimeZoneProviderEvent.createSuggestionEvent(suggestion);
if (!thisEvent.isEquivalentTo(mLastEventSent)) {
TimeZoneProviderEvent.createSuggestionEvent(
SystemClock.elapsedRealtime(), suggestion);
if (shouldSendEvent(thisEvent)) {
manager.onTimeZoneProviderEvent(thisEvent);
mLastEventSent = thisEvent;
}
@@ -231,8 +237,9 @@ public abstract class TimeZoneProviderService extends Service {
if (manager != null) {
try {
TimeZoneProviderEvent thisEvent =
TimeZoneProviderEvent.createUncertainEvent();
if (!thisEvent.isEquivalentTo(mLastEventSent)) {
TimeZoneProviderEvent.createUncertainEvent(
SystemClock.elapsedRealtime());
if (shouldSendEvent(thisEvent)) {
manager.onTimeZoneProviderEvent(thisEvent);
mLastEventSent = thisEvent;
}
@@ -258,8 +265,9 @@ public abstract class TimeZoneProviderService extends Service {
try {
String causeString = cause.getMessage();
TimeZoneProviderEvent thisEvent =
TimeZoneProviderEvent.createPermanentFailureEvent(causeString);
if (!thisEvent.isEquivalentTo(mLastEventSent)) {
TimeZoneProviderEvent.createPermanentFailureEvent(
SystemClock.elapsedRealtime(), causeString);
if (shouldSendEvent(thisEvent)) {
manager.onTimeZoneProviderEvent(thisEvent);
mLastEventSent = thisEvent;
}
@@ -271,10 +279,33 @@ public abstract class TimeZoneProviderService extends Service {
});
}
@GuardedBy("mLock")
private boolean shouldSendEvent(TimeZoneProviderEvent newEvent) {
// Always send an event if it indicates a state or suggestion change.
if (!newEvent.isEquivalentTo(mLastEventSent)) {
return true;
}
// Guard against implementations that generate a lot of uninteresting events in a short
// space of time and would cause the time_zone_detector to evaluate time zone suggestions
// too frequently.
//
// If the new event and last event sent are equivalent, the client will still send an update
// if their creation times are sufficiently different. This enables the time_zone_detector
// to better understand how recently the location time zone provider was certain /
// uncertain, which can be useful when working out ordering of events, e.g. to work out
// whether a suggestion was generated before or after a device left airplane mode.
long timeSinceLastEventMillis =
newEvent.getCreationElapsedMillis() - mLastEventSent.getCreationElapsedMillis();
return timeSinceLastEventMillis > mEventFilteringAgeThresholdMillis;
}
private void onStartUpdatesInternal(@NonNull ITimeZoneProviderManager manager,
@DurationMillisLong long initializationTimeoutMillis) {
@DurationMillisLong long initializationTimeoutMillis,
@DurationMillisLong long eventFilteringAgeThresholdMillis) {
synchronized (mLock) {
mManager = manager;
mEventFilteringAgeThresholdMillis = eventFilteringAgeThresholdMillis;
mLastEventSent = null;
onStartUpdates(initializationTimeoutMillis);
}
@@ -332,9 +363,11 @@ public abstract class TimeZoneProviderService extends Service {
private class TimeZoneProviderServiceWrapper extends ITimeZoneProvider.Stub {
public void startUpdates(@NonNull ITimeZoneProviderManager manager,
@DurationMillisLong long initializationTimeoutMillis) {
@DurationMillisLong long initializationTimeoutMillis,
@DurationMillisLong long eventFilteringAgeThresholdMillis) {
Objects.requireNonNull(manager);
mHandler.post(() -> onStartUpdatesInternal(manager, initializationTimeoutMillis));
mHandler.post(() -> onStartUpdatesInternal(
manager, initializationTimeoutMillis, eventFilteringAgeThresholdMillis));
}
public void stopUpdates() {

View File

@@ -18,6 +18,7 @@ package android.service.timezone;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -121,6 +122,24 @@ public final class TimeZoneProviderSuggestion implements Parcelable {
parcel.writeLong(mElapsedRealtimeMillis);
}
/**
* Similar to {@link #equals} except this methods checks for equivalence, not equality.
* i.e. two suggestions are equivalent if they suggest the same time zones.
*
* @hide
*/
@SuppressWarnings("ReferenceEquality")
public boolean isEquivalentTo(@Nullable TimeZoneProviderSuggestion other) {
if (this == other) {
return true;
}
if (other == null) {
return false;
}
// Only check the time zone IDs. The times can be different, but we don't mind.
return mTimeZoneIds.equals(other.mTimeZoneIds);
}
@Override
public boolean equals(Object o) {
if (this == o) {

View File

@@ -33,7 +33,8 @@ public class TimeZoneProviderEventTest {
@Test
public void isEquivalentToAndEquals() {
TimeZoneProviderEvent fail1v1 = TimeZoneProviderEvent.createPermanentFailureEvent("one");
TimeZoneProviderEvent fail1v1 =
TimeZoneProviderEvent.createPermanentFailureEvent(1111L, "one");
assertEquals(fail1v1, fail1v1);
assertIsEquivalentTo(fail1v1, fail1v1);
assertNotEquals(fail1v1, null);
@@ -41,58 +42,74 @@ public class TimeZoneProviderEventTest {
{
TimeZoneProviderEvent fail1v2 =
TimeZoneProviderEvent.createPermanentFailureEvent("one");
TimeZoneProviderEvent.createPermanentFailureEvent(1111L, "one");
assertEquals(fail1v1, fail1v2);
assertIsEquivalentTo(fail1v1, fail1v2);
TimeZoneProviderEvent fail2 = TimeZoneProviderEvent.createPermanentFailureEvent("two");
TimeZoneProviderEvent fail2 =
TimeZoneProviderEvent.createPermanentFailureEvent(2222L, "two");
assertNotEquals(fail1v1, fail2);
assertIsEquivalentTo(fail1v1, fail2);
}
TimeZoneProviderEvent uncertain1v1 = TimeZoneProviderEvent.createUncertainEvent();
TimeZoneProviderEvent uncertain1v1 = TimeZoneProviderEvent.createUncertainEvent(1111L);
assertEquals(uncertain1v1, uncertain1v1);
assertIsEquivalentTo(uncertain1v1, uncertain1v1);
assertNotEquals(uncertain1v1, null);
assertNotEquivalentTo(uncertain1v1, null);
{
TimeZoneProviderEvent uncertain1v2 = TimeZoneProviderEvent.createUncertainEvent();
TimeZoneProviderEvent uncertain1v2 = TimeZoneProviderEvent.createUncertainEvent(1111L);
assertEquals(uncertain1v1, uncertain1v2);
assertIsEquivalentTo(uncertain1v1, uncertain1v2);
TimeZoneProviderEvent uncertain2 = TimeZoneProviderEvent.createUncertainEvent(2222L);
assertNotEquals(uncertain1v1, uncertain2);
assertIsEquivalentTo(uncertain1v1, uncertain2);
}
TimeZoneProviderSuggestion suggestion1 = new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(1111L)
.setTimeZoneIds(Collections.singletonList("Europe/London"))
.build();
TimeZoneProviderEvent certain1v1 = TimeZoneProviderEvent.createSuggestionEvent(suggestion1);
TimeZoneProviderEvent certain1v1 =
TimeZoneProviderEvent.createSuggestionEvent(1111L, suggestion1);
assertEquals(certain1v1, certain1v1);
assertIsEquivalentTo(certain1v1, certain1v1);
assertNotEquals(certain1v1, null);
assertNotEquivalentTo(certain1v1, null);
{
// Same suggestion, same time.
TimeZoneProviderEvent certain1v2 =
TimeZoneProviderEvent.createSuggestionEvent(suggestion1);
TimeZoneProviderEvent.createSuggestionEvent(1111L, suggestion1);
assertEquals(certain1v1, certain1v2);
assertIsEquivalentTo(certain1v1, certain1v2);
// Same suggestion, different time.
TimeZoneProviderEvent certain1v3 =
TimeZoneProviderEvent.createSuggestionEvent(2222L, suggestion1);
assertNotEquals(certain1v1, certain1v3);
assertIsEquivalentTo(certain1v1, certain1v3);
// suggestion1 is equivalent to suggestion2, but not equal
TimeZoneProviderSuggestion suggestion2 = new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(2222L)
.setTimeZoneIds(Collections.singletonList("Europe/London"))
.build();
assertNotEquals(suggestion1, suggestion2);
TimeZoneProviderSuggestionTest.assertIsEquivalentTo(suggestion1, suggestion2);
TimeZoneProviderEvent certain2 =
TimeZoneProviderEvent.createSuggestionEvent(suggestion2);
TimeZoneProviderEvent.createSuggestionEvent(2222L, suggestion2);
assertNotEquals(certain1v1, certain2);
assertIsEquivalentTo(certain1v1, certain2);
// suggestion3 is not equivalent to suggestion1
TimeZoneProviderSuggestion suggestion3 = new TimeZoneProviderSuggestion.Builder()
.setTimeZoneIds(Collections.singletonList("Europe/Paris"))
.build();
TimeZoneProviderEvent certain3 =
TimeZoneProviderEvent.createSuggestionEvent(suggestion3);
TimeZoneProviderEvent.createSuggestionEvent(2222L, suggestion3);
assertNotEquals(certain1v1, certain3);
assertNotEquivalentTo(certain1v1, certain3);
}
@@ -107,13 +124,13 @@ public class TimeZoneProviderEventTest {
@Test
public void testParcelable_failureEvent() {
TimeZoneProviderEvent event =
TimeZoneProviderEvent.createPermanentFailureEvent("failure reason");
TimeZoneProviderEvent.createPermanentFailureEvent(1111L, "failure reason");
assertRoundTripParcelable(event);
}
@Test
public void testParcelable_uncertain() {
TimeZoneProviderEvent event = TimeZoneProviderEvent.createUncertainEvent();
TimeZoneProviderEvent event = TimeZoneProviderEvent.createUncertainEvent(1111L);
assertRoundTripParcelable(event);
}
@@ -122,7 +139,8 @@ public class TimeZoneProviderEventTest {
TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
.setTimeZoneIds(Arrays.asList("Europe/London", "Europe/Paris"))
.build();
TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(suggestion);
TimeZoneProviderEvent event =
TimeZoneProviderEvent.createSuggestionEvent(1111L, suggestion);
assertRoundTripParcelable(event);
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2021 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 android.service.timezone;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
import java.util.Collections;
/** Tests for non-API methods */
public class TimeZoneProviderSuggestionTest {
@Test
public void isEquivalentToAndEquals() {
TimeZoneProviderSuggestion suggestion1 = new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(1111L)
.setTimeZoneIds(Collections.singletonList("Europe/London"))
.build();
assertEquals(suggestion1, suggestion1);
assertIsEquivalentTo(suggestion1, suggestion1);
assertNotEquals(suggestion1, null);
assertNotEquivalentTo(suggestion1, null);
// Same time zone IDs, different time.
TimeZoneProviderSuggestion suggestion2 = new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(2222L)
.setTimeZoneIds(Collections.singletonList("Europe/London"))
.build();
assertNotEquals(suggestion1, suggestion2);
assertIsEquivalentTo(suggestion1, suggestion2);
// Different time zone IDs.
TimeZoneProviderSuggestion suggestion3 = new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(1111L)
.setTimeZoneIds(Collections.singletonList("Europe/Paris"))
.build();
assertNotEquals(suggestion1, suggestion3);
assertNotEquivalentTo(suggestion1, suggestion3);
}
static void assertNotEquivalentTo(
TimeZoneProviderSuggestion one, TimeZoneProviderSuggestion two) {
if (one == null && two == null) {
fail("null arguments");
}
if (one != null) {
assertFalse("one=" + one + ", two=" + two, one.isEquivalentTo(two));
}
if (two != null) {
assertFalse("one=" + one + ", two=" + two, two.isEquivalentTo(one));
}
}
static void assertIsEquivalentTo(
TimeZoneProviderSuggestion one, TimeZoneProviderSuggestion two) {
if (one == null || two == null) {
fail("null arguments");
}
assertTrue("one=" + one + ", two=" + two, one.isEquivalentTo(two));
assertTrue("one=" + one + ", two=" + two, two.isEquivalentTo(one));
}
}

View File

@@ -55,10 +55,11 @@ public final class ServerFlags {
*/
@StringDef(prefix = "KEY_", value = {
KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
KEY_PRIMARY_LTZP_MODE_OVERRIDE,
KEY_SECONDARY_LTZP_MODE_OVERRIDE,
KEY_LTZP_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
KEY_LTZP_INITIALIZATION_TIMEOUT_MILLIS,
KEY_LTZP_EVENT_FILTERING_AGE_THRESHOLD_MILLIS,
KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
@@ -82,16 +83,14 @@ public final class ServerFlags {
* The key for the server flag that can override the device config for whether the primary
* location time zone provider is enabled, disabled, or (for testing) in simulation mode.
*/
public static final @DeviceConfigKey String
KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE =
public static final @DeviceConfigKey String KEY_PRIMARY_LTZP_MODE_OVERRIDE =
"primary_location_time_zone_provider_mode_override";
/**
* The key for the server flag that can override the device config for whether the secondary
* location time zone provider is enabled or disabled, or (for testing) in simulation mode.
*/
public static final @DeviceConfigKey String
KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE =
public static final @DeviceConfigKey String KEY_SECONDARY_LTZP_MODE_OVERRIDE =
"secondary_location_time_zone_provider_mode_override";
/**
@@ -106,18 +105,20 @@ public final class ServerFlags {
* The key for the timeout passed to a location time zone provider that tells it how long it has
* to provide an explicit first suggestion without being declared uncertain.
*/
public static final @DeviceConfigKey String
KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS =
"ltpz_init_timeout_millis";
public static final @DeviceConfigKey String KEY_LTZP_INITIALIZATION_TIMEOUT_MILLIS =
"ltzp_init_timeout_millis";
/**
* The key for the extra time added to {@link
* #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone
* #KEY_LTZP_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone
* manager before the location time zone provider will actually be declared uncertain.
*/
public static final @DeviceConfigKey String
KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS =
"ltpz_init_timeout_fuzz_millis";
public static final @DeviceConfigKey String KEY_LTZP_INITIALIZATION_TIMEOUT_FUZZ_MILLIS =
"ltzp_init_timeout_fuzz_millis";
/** The key for the setting that controls rate limiting of provider events. */
public static final @DeviceConfigKey String KEY_LTZP_EVENT_FILTERING_AGE_THRESHOLD_MILLIS =
"ltzp_event_filtering_age_threshold_millis";
/**
* The key for the server flag that can override location time zone detection being enabled for

View File

@@ -72,17 +72,18 @@ public final class ServiceConfigAccessor {
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
ServerFlags.KEY_PRIMARY_LTZP_MODE_OVERRIDE,
ServerFlags.KEY_SECONDARY_LTZP_MODE_OVERRIDE,
ServerFlags.KEY_LTZP_INITIALIZATION_TIMEOUT_MILLIS,
ServerFlags.KEY_LTZP_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
ServerFlags.KEY_LTZP_EVENT_FILTERING_AGE_THRESHOLD_MILLIS,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS
}));
private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
Duration.ofMinutes(1);
private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
private static final Duration DEFAULT_LTZP_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
private static final Duration DEFAULT_LTZP_INITIALIZATION_TIMEOUT_FUZZ = Duration.ofMinutes(1);
private static final Duration DEFAULT_LTZP_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
private static final Duration DEFAULT_LTZP_EVENT_FILTER_AGE_THRESHOLD = Duration.ofMinutes(1);
private static final Object SLOCK = new Object();
@@ -326,8 +327,7 @@ public final class ServiceConfigAccessor {
// In test mode: use the test setting value.
return mTestPrimaryLocationTimeZoneProviderMode;
}
return mServerFlags.getOptionalString(
ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE)
return mServerFlags.getOptionalString(ServerFlags.KEY_PRIMARY_LTZP_MODE_OVERRIDE)
.orElse(getPrimaryLocationTimeZoneProviderModeFromConfig());
}
@@ -346,8 +346,7 @@ public final class ServiceConfigAccessor {
// In test mode: use the test setting value.
return mTestSecondaryLocationTimeZoneProviderMode;
}
return mServerFlags.getOptionalString(
ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE)
return mServerFlags.getOptionalString(ServerFlags.KEY_SECONDARY_LTZP_MODE_OVERRIDE)
.orElse(getSecondaryLocationTimeZoneProviderModeFromConfig());
}
@@ -385,8 +384,8 @@ public final class ServiceConfigAccessor {
@NonNull
public Duration getLocationTimeZoneProviderInitializationTimeout() {
return mServerFlags.getDurationFromMillis(
ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT);
ServerFlags.KEY_LTZP_INITIALIZATION_TIMEOUT_MILLIS,
DEFAULT_LTZP_INITIALIZATION_TIMEOUT);
}
/**
@@ -396,8 +395,8 @@ public final class ServiceConfigAccessor {
@NonNull
public Duration getLocationTimeZoneProviderInitializationTimeoutFuzz() {
return mServerFlags.getDurationFromMillis(
ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ);
ServerFlags.KEY_LTZP_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
DEFAULT_LTZP_INITIALIZATION_TIMEOUT_FUZZ);
}
/**
@@ -408,7 +407,18 @@ public final class ServiceConfigAccessor {
public Duration getLocationTimeZoneUncertaintyDelay() {
return mServerFlags.getDurationFromMillis(
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
DEFAULT_PROVIDER_UNCERTAINTY_DELAY);
DEFAULT_LTZP_UNCERTAINTY_DELAY);
}
/**
* Returns the time between equivalent events before the provider process will send the event
* to the system server.
*/
@NonNull
public Duration getLocationTimeZoneProviderEventFilteringAgeThreshold() {
return mServerFlags.getDurationFromMillis(
ServerFlags.KEY_LTZP_EVENT_FILTERING_AGE_THRESHOLD_MILLIS,
DEFAULT_LTZP_EVENT_FILTER_AGE_THRESHOLD);
}
/** Clears all in-memory test config. */

View File

@@ -112,11 +112,12 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
}
@Override
void onStartUpdates(@NonNull Duration initializationTimeout) {
void onStartUpdates(@NonNull Duration initializationTimeout,
@NonNull Duration eventFilteringAgeThreshold) {
// 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.
TimeZoneProviderRequest request =
TimeZoneProviderRequest.createStartUpdatesRequest(initializationTimeout);
TimeZoneProviderRequest request = TimeZoneProviderRequest.createStartUpdatesRequest(
initializationTimeout, eventFilteringAgeThreshold);
mProxy.setRequest(request);
}

View File

@@ -78,4 +78,9 @@ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Envir
Duration getUncertaintyDelay() {
return mServiceConfigAccessor.getLocationTimeZoneUncertaintyDelay();
}
@Override
Duration getProviderEventFilteringAgeThreshold() {
return mServiceConfigAccessor.getLocationTimeZoneProviderEventFilteringAgeThreshold();
}
}

View File

@@ -283,6 +283,7 @@ class ControllerImpl extends LocationTimeZoneProviderController {
}
}
@GuardedBy("mSharedLock")
private void tryStartProvider(@NonNull LocationTimeZoneProvider provider,
@NonNull ConfigurationInternal configuration) {
ProviderState providerState = provider.getCurrentState();
@@ -291,7 +292,8 @@ class ControllerImpl extends LocationTimeZoneProviderController {
debugLog("Enabling " + provider);
provider.startUpdates(configuration,
mEnvironment.getProviderInitializationTimeout(),
mEnvironment.getProviderInitializationTimeoutFuzz());
mEnvironment.getProviderInitializationTimeoutFuzz(),
mEnvironment.getProviderEventFilteringAgeThreshold());
break;
}
case PROVIDER_STATE_STARTED_INITIALIZING:

View File

@@ -26,10 +26,11 @@ import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_STOP;
import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS;
import static com.android.server.timedetector.ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
import static com.android.server.timedetector.ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
import static com.android.server.timedetector.ServerFlags.KEY_LTZP_EVENT_FILTERING_AGE_THRESHOLD_MILLIS;
import static com.android.server.timedetector.ServerFlags.KEY_LTZP_INITIALIZATION_TIMEOUT_FUZZ_MILLIS;
import static com.android.server.timedetector.ServerFlags.KEY_LTZP_INITIALIZATION_TIMEOUT_MILLIS;
import static com.android.server.timedetector.ServerFlags.KEY_PRIMARY_LTZP_MODE_OVERRIDE;
import static com.android.server.timedetector.ServerFlags.KEY_SECONDARY_LTZP_MODE_OVERRIDE;
import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_DISABLED;
import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_ENABLED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
@@ -126,20 +127,23 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
pw.println();
pw.printf("This service is also affected by the following device_config flags in the"
+ " %s namespace:\n", NAMESPACE_SYSTEM_TIME);
pw.printf(" %s\n", KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE);
pw.printf(" %s\n", KEY_PRIMARY_LTZP_MODE_OVERRIDE);
pw.printf(" Overrides the mode of the primary provider. Values=%s|%s\n",
PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED);
pw.printf(" %s\n", KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE);
pw.printf(" %s\n", KEY_SECONDARY_LTZP_MODE_OVERRIDE);
pw.printf(" Overrides the mode of the secondary provider. Values=%s|%s\n",
PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED);
pw.printf(" %s\n", KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS);
pw.printf(" Sets the amount of time the service waits when uncertain before making an"
+ " 'uncertain' suggestion to the time zone detector.\n");
pw.printf(" %s\n", KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS);
pw.printf(" %s\n", KEY_LTZP_INITIALIZATION_TIMEOUT_MILLIS);
pw.printf(" Sets the initialization time passed to the providers.\n");
pw.printf(" %s\n", KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS);
pw.printf(" %s\n", KEY_LTZP_INITIALIZATION_TIMEOUT_FUZZ_MILLIS);
pw.printf(" Sets the amount of extra time added to the providers' initialization time."
+ "\n");
pw.printf(" %s\n", KEY_LTZP_EVENT_FILTERING_AGE_THRESHOLD_MILLIS);
pw.printf(" Sets the amount of time that must pass between equivalent LTZP events before"
+ " they will be reported to the system server.\n");
pw.println();
pw.printf("Typically, use '%s' to stop the service before setting individual"
+ " flags and '%s' after to restart it.\n",

View File

@@ -532,7 +532,8 @@ abstract class LocationTimeZoneProvider implements Dumpable {
* called using the handler thread from the {@link ThreadingDomain}.
*/
final void startUpdates(@NonNull ConfigurationInternal currentUserConfiguration,
@NonNull Duration initializationTimeout, @NonNull Duration initializationTimeoutFuzz) {
@NonNull Duration initializationTimeout, @NonNull Duration initializationTimeoutFuzz,
@NonNull Duration eventFilteringAgeThreshold) {
mThreadingDomain.assertCurrentThread();
synchronized (mSharedLock) {
@@ -548,7 +549,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
mInitializationTimeoutQueue.runDelayed(
this::handleInitializationTimeout, delay.toMillis());
onStartUpdates(initializationTimeout);
onStartUpdates(initializationTimeout, eventFilteringAgeThreshold);
}
}
@@ -574,9 +575,11 @@ abstract class LocationTimeZoneProvider implements Dumpable {
* Implemented by subclasses to do work during {@link #startUpdates}. This is where the logic
* to start the real provider should be implemented.
*
* @param initializationTimeout the initialization timeout to pass to the real provider
* @param initializationTimeout the initialization timeout to pass to the provider
* @param eventFilteringAgeThreshold the event filtering age threshold to pass to the provider
*/
abstract void onStartUpdates(@NonNull Duration initializationTimeout);
abstract void onStartUpdates(@NonNull Duration initializationTimeout,
@NonNull Duration eventFilteringAgeThreshold);
/**
* Stops the provider. It is an error to call this method except when the {@link

View File

@@ -129,6 +129,12 @@ abstract class LocationTimeZoneProviderController implements Dumpable {
*/
abstract Duration getProviderInitializationTimeoutFuzz();
/**
* Returns the value passed to LocationTimeZoneProviders to control rate limiting of
* equivalent events.
*/
abstract Duration getProviderEventFilteringAgeThreshold();
/**
* Returns the delay allowed after receiving uncertainty from a provider before it should be
* passed on.

View File

@@ -19,6 +19,7 @@ package com.android.server.timezonedetector.location;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.SystemClock;
import android.service.timezone.TimeZoneProviderEvent;
import android.util.IndentingPrintWriter;
@@ -59,7 +60,7 @@ class NullLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
void setRequest(@NonNull TimeZoneProviderRequest request) {
if (request.sendUpdates()) {
TimeZoneProviderEvent event = TimeZoneProviderEvent.createPermanentFailureEvent(
"Provider is disabled");
SystemClock.elapsedRealtime(), "Provider is disabled");
handleTimeZoneProviderEvent(event);
}
}

View File

@@ -152,7 +152,9 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy im
mServiceWatcher.runOnBinder(binder -> {
ITimeZoneProvider service = ITimeZoneProvider.Stub.asInterface(binder);
if (request.sendUpdates()) {
service.startUpdates(managerProxy, request.getInitializationTimeout().toMillis());
service.startUpdates(managerProxy,
request.getInitializationTimeout().toMillis(),
request.getEventFilteringAgeThreshold().toMillis());
} else {
service.stopUpdates();
}

View File

@@ -31,23 +31,32 @@ final class TimeZoneProviderRequest {
private static final TimeZoneProviderRequest STOP_UPDATES =
new TimeZoneProviderRequest(
false /* sendUpdates */,
null /* initializationTimeout */);
null /* initializationTimeout */,
null /* eventFilteringAgeThreshold */);
private final boolean mSendUpdates;
@Nullable
private final Duration mInitializationTimeout;
@Nullable
private final Duration mEventFilteringAgeThreshold;
private TimeZoneProviderRequest(
boolean sendUpdates, @Nullable Duration initializationTimeout) {
boolean sendUpdates, @Nullable Duration initializationTimeout,
@Nullable Duration eventFilteringAgeThreshold) {
mSendUpdates = sendUpdates;
mInitializationTimeout = initializationTimeout;
mEventFilteringAgeThreshold = eventFilteringAgeThreshold;
}
/** Creates a request to start updates with the specified timeout. */
public static TimeZoneProviderRequest createStartUpdatesRequest(
@NonNull Duration initializationTimeout) {
return new TimeZoneProviderRequest(true, Objects.requireNonNull(initializationTimeout));
@NonNull Duration initializationTimeout,
@NonNull Duration eventFilteringAgeThreshold) {
return new TimeZoneProviderRequest(true,
Objects.requireNonNull(initializationTimeout),
Objects.requireNonNull(eventFilteringAgeThreshold));
}
/** Creates a request to stop updates. */
@@ -74,6 +83,17 @@ final class TimeZoneProviderRequest {
return mInitializationTimeout;
}
/**
* Returns the threshold the remote process is to use to filter equivalent events. Only valid
* when {@link #sendUpdates()} is {@code true}.
*
* <p>Guaranteed to be set when {@link #sendUpdates()} returns {@code true}.
*/
@NonNull
public Duration getEventFilteringAgeThreshold() {
return mEventFilteringAgeThreshold;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -82,15 +102,15 @@ final class TimeZoneProviderRequest {
if (o == null || getClass() != o.getClass()) {
return false;
}
TimeZoneProviderRequest
that = (TimeZoneProviderRequest) o;
TimeZoneProviderRequest that = (TimeZoneProviderRequest) o;
return mSendUpdates == that.mSendUpdates
&& mInitializationTimeout == that.mInitializationTimeout;
&& Objects.equals(mInitializationTimeout, that.mInitializationTimeout)
&& Objects.equals(mEventFilteringAgeThreshold, that.mEventFilteringAgeThreshold);
}
@Override
public int hashCode() {
return Objects.hash(mSendUpdates, mInitializationTimeout);
return Objects.hash(mSendUpdates, mInitializationTimeout, mEventFilteringAgeThreshold);
}
@Override
@@ -98,6 +118,7 @@ final class TimeZoneProviderRequest {
return "TimeZoneProviderRequest{"
+ "mSendUpdates=" + mSendUpdates
+ ", mInitializationTimeout=" + mInitializationTimeout
+ ", mEventFilteringAgeThreshold=" + mEventFilteringAgeThreshold
+ "}";
}
}

View File

@@ -53,7 +53,7 @@ public class ZoneInfoDbTimeZoneProviderEventPreProcessor
// enables immediate failover to a secondary provider, one that might provide valid IDs for
// the same location, which should provide better behavior than just ignoring the event.
if (hasInvalidZones(event)) {
return TimeZoneProviderEvent.createUncertainEvent();
return TimeZoneProviderEvent.createUncertainEvent(event.getCreationElapsedMillis());
}
return event;

View File

@@ -66,9 +66,9 @@ public class ControllerImplTest {
private static final TimeZoneProviderEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2 =
createSuggestionEvent(asList("Europe/Paris"));
private static final TimeZoneProviderEvent USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT =
TimeZoneProviderEvent.createUncertainEvent();
TimeZoneProviderEvent.createUncertainEvent(ARBITRARY_TIME_MILLIS);
private static final TimeZoneProviderEvent USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT =
TimeZoneProviderEvent.createPermanentFailureEvent("Test");
TimeZoneProviderEvent.createPermanentFailureEvent(ARBITRARY_TIME_MILLIS, "Test");
private TestThreadingDomain mTestThreadingDomain;
private TestCallback mTestCallback;
@@ -1125,6 +1125,7 @@ public class ControllerImplTest {
private static TimeZoneProviderEvent createSuggestionEvent(@NonNull List<String> timeZoneIds) {
return TimeZoneProviderEvent.createSuggestionEvent(
ARBITRARY_TIME_MILLIS,
new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(ARBITRARY_TIME_MILLIS)
.setTimeZoneIds(timeZoneIds)
@@ -1137,10 +1138,13 @@ public class ControllerImplTest {
// (initialization timeout * 2) < uncertainty delay
//
// That makes the order of initialization timeout Vs uncertainty delay deterministic.
static final Duration PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
static final Duration PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = Duration.ofMinutes(1);
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 UNCERTAINTY_DELAY = Duration.ofMinutes(15);
private static final Duration PROVIDER_EVENT_FILTERING_AGE_THRESHOLD =
Duration.ofMinutes(3);
private final LocationTimeZoneProviderController mController;
private ConfigurationInternal mConfigurationInternal;
@@ -1172,6 +1176,11 @@ public class ControllerImplTest {
return PROVIDER_INITIALIZATION_TIMEOUT_FUZZ;
}
@Override
Duration getProviderEventFilteringAgeThreshold() {
return PROVIDER_EVENT_FILTERING_AGE_THRESHOLD;
}
@Override
Duration getUncertaintyDelay() {
return UNCERTAINTY_DELAY;
@@ -1255,7 +1264,7 @@ public class ControllerImplTest {
}
@Override
void onStartUpdates(Duration initializationTimeout) {
void onStartUpdates(Duration initializationTimeout, Duration eventFilteringAgeThreshold) {
// Nothing needed for tests.
}

View File

@@ -30,7 +30,8 @@ public final class FakeTimeZoneProviderEventPreProcessor
@Override
public TimeZoneProviderEvent preProcess(TimeZoneProviderEvent timeZoneProviderEvent) {
if (mIsUncertain) {
return TimeZoneProviderEvent.createUncertainEvent();
return TimeZoneProviderEvent.createUncertainEvent(
timeZoneProviderEvent.getCreationElapsedMillis());
}
return timeZoneProviderEvent;
}

View File

@@ -92,10 +92,12 @@ public class LocationTimeZoneProviderTest {
ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
Duration arbitraryInitializationTimeoutFuzz = Duration.ofMinutes(2);
Duration arbitraryEventFilteringAgeThreshold = Duration.ofMinutes(3);
provider.startUpdates(config, arbitraryInitializationTimeout,
arbitraryInitializationTimeoutFuzz);
arbitraryInitializationTimeoutFuzz, arbitraryEventFilteringAgeThreshold);
provider.assertOnStartCalled(arbitraryInitializationTimeout);
provider.assertOnStartCalled(
arbitraryInitializationTimeout, arbitraryEventFilteringAgeThreshold);
currentState = assertAndReturnProviderState(
provider, providerMetricsLogger, PROVIDER_STATE_STARTED_INITIALIZING);
@@ -118,7 +120,8 @@ public class LocationTimeZoneProviderTest {
.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
.setTimeZoneIds(Arrays.asList("Europe/London"))
.build();
TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(suggestion);
TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(
ARBITRARY_ELAPSED_REALTIME_MILLIS, suggestion);
provider.simulateProviderEventReceived(event);
currentState = assertAndReturnProviderState(
@@ -130,7 +133,7 @@ public class LocationTimeZoneProviderTest {
mProviderListener.assertProviderChangeReported(PROVIDER_STATE_STARTED_CERTAIN);
// Simulate an uncertain event being received.
event = TimeZoneProviderEvent.createUncertainEvent();
event = TimeZoneProviderEvent.createUncertainEvent(ARBITRARY_ELAPSED_REALTIME_MILLIS);
provider.simulateProviderEventReceived(event);
currentState = assertAndReturnProviderState(
@@ -179,8 +182,9 @@ public class LocationTimeZoneProviderTest {
ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
Duration arbitraryInitializationTimeoutFuzz = Duration.ofMinutes(2);
Duration eventFilteringAgeThreshold = Duration.ofMinutes(3);
provider.startUpdates(config, arbitraryInitializationTimeout,
arbitraryInitializationTimeoutFuzz);
arbitraryInitializationTimeoutFuzz, eventFilteringAgeThreshold);
provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_INITIALIZING);
// Simulate a suggestion event being received.
@@ -188,12 +192,13 @@ public class LocationTimeZoneProviderTest {
.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
.setTimeZoneIds(Arrays.asList("Europe/London"))
.build();
TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(suggestion);
TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(
ARBITRARY_ELAPSED_REALTIME_MILLIS, suggestion);
provider.simulateProviderEventReceived(event);
provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_CERTAIN);
// Simulate an uncertain event being received.
event = TimeZoneProviderEvent.createUncertainEvent();
event = TimeZoneProviderEvent.createUncertainEvent(ARBITRARY_ELAPSED_REALTIME_MILLIS);
provider.simulateProviderEventReceived(event);
provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_UNCERTAIN);
@@ -221,16 +226,17 @@ public class LocationTimeZoneProviderTest {
ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
Duration arbitraryInitializationTimeoutFuzz = Duration.ofMinutes(2);
Duration eventFilteringAgeThreshold = Duration.ofMinutes(3);
provider.startUpdates(config, arbitraryInitializationTimeout,
arbitraryInitializationTimeoutFuzz);
arbitraryInitializationTimeoutFuzz, eventFilteringAgeThreshold);
List<String> invalidTimeZoneIds = asList("Atlantic/Atlantis");
TimeZoneProviderSuggestion invalidIdSuggestion = new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
.setTimeZoneIds(invalidTimeZoneIds)
.build();
TimeZoneProviderEvent event =
TimeZoneProviderEvent.createSuggestionEvent(invalidIdSuggestion);
TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(
ARBITRARY_ELAPSED_REALTIME_MILLIS, invalidIdSuggestion);
provider.simulateProviderEventReceived(event);
provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_UNCERTAIN);
}
@@ -278,6 +284,7 @@ public class LocationTimeZoneProviderTest {
private boolean mOnDestroyCalled;
private boolean mOnStartUpdatesCalled;
private Duration mInitializationTimeout;
private Duration mEventFilteringAgeThreshold;
private boolean mOnStopUpdatesCalled;
/** Creates the instance. */
@@ -301,9 +308,11 @@ public class LocationTimeZoneProviderTest {
}
@Override
void onStartUpdates(@NonNull Duration initializationTimeout) {
void onStartUpdates(@NonNull Duration initializationTimeout,
@NonNull Duration eventFilteringAgeThreshold) {
mOnStartUpdatesCalled = true;
mInitializationTimeout = initializationTimeout;
mEventFilteringAgeThreshold = eventFilteringAgeThreshold;
}
@Override
@@ -320,9 +329,11 @@ public class LocationTimeZoneProviderTest {
assertTrue(mOnInitializeCalled);
}
void assertOnStartCalled(Duration expectedInitializationTimeout) {
void assertOnStartCalled(Duration expectedInitializationTimeout,
Duration eventFilteringAgeThreshold) {
assertTrue(mOnStartUpdatesCalled);
assertEquals(expectedInitializationTimeout, mInitializationTimeout);
assertEquals(eventFilteringAgeThreshold, mEventFilteringAgeThreshold);
}
void simulateProviderEventReceived(TimeZoneProviderEvent event) {

View File

@@ -54,14 +54,17 @@ public class ZoneInfoDbTimeZoneProviderEventPreProcessorTest {
for (String timeZone : nonExistingTimeZones) {
TimeZoneProviderEvent event = timeZoneProviderEvent(timeZone);
TimeZoneProviderEvent expectedResultEvent =
TimeZoneProviderEvent.createUncertainEvent(event.getCreationElapsedMillis());
assertWithMessage(timeZone + " is not a valid time zone")
.that(mPreProcessor.preProcess(event))
.isEqualTo(TimeZoneProviderEvent.createUncertainEvent());
.isEqualTo(expectedResultEvent);
}
}
private static TimeZoneProviderEvent timeZoneProviderEvent(String... timeZoneIds) {
return TimeZoneProviderEvent.createSuggestionEvent(
ARBITRARY_TIME_MILLIS,
new TimeZoneProviderSuggestion.Builder()
.setTimeZoneIds(Arrays.asList(timeZoneIds))
.setElapsedRealtimeMillis(ARBITRARY_TIME_MILLIS)