From 13c1a5cf4f00f0f80ae017c0cf53ce82a6120530 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Fri, 21 Oct 2022 11:16:10 +0100 Subject: Changes to support time zone provider status APIs These changes add a new status (not yet part of the SDK API) that will enable the time zone detection service to make decisions like telephony fallback based on information from the provider. The status will also enable information to ultimately be surfaced to the user in SettingsUI about the time zone detection being blocked or running degraded due to settings like the "Google Location Accuracy" setting on GMS device. The SDK API changes will be made once the information passed is used and can actually be detected from tests. Test: atest core/tests/coretests/src/android/service/timezone/ Test: atest services/tests/servicestests/src/com/android/server/timezonedetector/location/ Bug: 236624675 Change-Id: Ia1a9f9a18454fc087d187289d761d5e3649bbaee --- .../service/timezone/TimeZoneProviderEvent.java | 104 ++++--- .../service/timezone/TimeZoneProviderService.java | 33 +- .../service/timezone/TimeZoneProviderStatus.aidl | 22 ++ .../service/timezone/TimeZoneProviderStatus.java | 336 +++++++++++++++++++++ .../timezone/TimeZoneProviderEventTest.java | 102 +++++-- .../timezone/TimeZoneProviderStatusTest.java | 110 +++++++ ...oneInfoDbTimeZoneProviderEventPreProcessor.java | 10 +- .../FakeTimeZoneProviderEventPreProcessor.java | 4 +- .../LocationTimeZoneProviderControllerTest.java | 31 +- .../location/LocationTimeZoneProviderTest.java | 15 +- ...nfoDbTimeZoneProviderEventPreProcessorTest.java | 28 +- 11 files changed, 718 insertions(+), 77 deletions(-) create mode 100644 core/java/android/service/timezone/TimeZoneProviderStatus.aidl create mode 100644 core/java/android/service/timezone/TimeZoneProviderStatus.java create mode 100644 core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java diff --git a/core/java/android/service/timezone/TimeZoneProviderEvent.java b/core/java/android/service/timezone/TimeZoneProviderEvent.java index f6433b7f371e..714afee197f1 100644 --- a/core/java/android/service/timezone/TimeZoneProviderEvent.java +++ b/core/java/android/service/timezone/TimeZoneProviderEvent.java @@ -57,7 +57,7 @@ public final class TimeZoneProviderEvent implements Parcelable { /** * The provider was uncertain about the time zone. See {@link - * TimeZoneProviderService#reportUncertain()} + * TimeZoneProviderService#reportUncertain(TimeZoneProviderStatus)} */ public static final @EventType int EVENT_TYPE_UNCERTAIN = 3; @@ -66,42 +66,55 @@ public final class TimeZoneProviderEvent implements Parcelable { @ElapsedRealtimeLong private final long mCreationElapsedMillis; + // Populated when mType == EVENT_TYPE_SUGGESTION @Nullable private final TimeZoneProviderSuggestion mSuggestion; + // Populated when mType == EVENT_TYPE_PERMANENT_FAILURE @Nullable private final String mFailureCause; - private TimeZoneProviderEvent(@EventType int type, + // Populated when mType == EVENT_TYPE_SUGGESTION or EVENT_TYPE_UNCERTAIN + @Nullable + private final TimeZoneProviderStatus mTimeZoneProviderStatus; + + private TimeZoneProviderEvent(int type, @ElapsedRealtimeLong long creationElapsedMillis, @Nullable TimeZoneProviderSuggestion suggestion, - @Nullable String failureCause) { + @Nullable String failureCause, + @Nullable TimeZoneProviderStatus timeZoneProviderStatus) { mType = type; mCreationElapsedMillis = creationElapsedMillis; mSuggestion = suggestion; mFailureCause = failureCause; + mTimeZoneProviderStatus = timeZoneProviderStatus; } - /** Returns a event of type {@link #EVENT_TYPE_SUGGESTION}. */ + /** Returns an event of type {@link #EVENT_TYPE_SUGGESTION}. */ public static TimeZoneProviderEvent createSuggestionEvent( @ElapsedRealtimeLong long creationElapsedMillis, - @NonNull TimeZoneProviderSuggestion suggestion) { + @NonNull TimeZoneProviderSuggestion suggestion, + @NonNull TimeZoneProviderStatus providerStatus) { return new TimeZoneProviderEvent(EVENT_TYPE_SUGGESTION, creationElapsedMillis, - Objects.requireNonNull(suggestion), null); + Objects.requireNonNull(suggestion), null, Objects.requireNonNull(providerStatus)); } - /** Returns a event of type {@link #EVENT_TYPE_UNCERTAIN}. */ + /** Returns an event of type {@link #EVENT_TYPE_UNCERTAIN}. */ public static TimeZoneProviderEvent createUncertainEvent( - @ElapsedRealtimeLong long creationElapsedMillis) { - return new TimeZoneProviderEvent(EVENT_TYPE_UNCERTAIN, creationElapsedMillis, null, null); + @ElapsedRealtimeLong long creationElapsedMillis, + @NonNull TimeZoneProviderStatus timeZoneProviderStatus) { + + return new TimeZoneProviderEvent( + EVENT_TYPE_UNCERTAIN, creationElapsedMillis, null, null, + Objects.requireNonNull(timeZoneProviderStatus)); } - /** Returns a event of type {@link #EVENT_TYPE_PERMANENT_FAILURE}. */ + /** Returns an event of type {@link #EVENT_TYPE_PERMANENT_FAILURE}. */ public static TimeZoneProviderEvent createPermanentFailureEvent( @ElapsedRealtimeLong long creationElapsedMillis, @NonNull String cause) { return new TimeZoneProviderEvent(EVENT_TYPE_PERMANENT_FAILURE, creationElapsedMillis, null, - Objects.requireNonNull(cause)); + Objects.requireNonNull(cause), null); } /** @@ -126,7 +139,7 @@ public final class TimeZoneProviderEvent implements Parcelable { } /** - * Returns the failure cauese. Populated when {@link #getType()} is {@link + * Returns the failure cause. Populated when {@link #getType()} is {@link * #EVENT_TYPE_PERMANENT_FAILURE}. */ @Nullable @@ -134,24 +147,34 @@ public final class TimeZoneProviderEvent implements Parcelable { return mFailureCause; } - public static final @NonNull Creator CREATOR = - new Creator() { - @Override - public TimeZoneProviderEvent createFromParcel(Parcel in) { - int type = in.readInt(); - long creationElapsedMillis = in.readLong(); - TimeZoneProviderSuggestion suggestion = - in.readParcelable(getClass().getClassLoader(), android.service.timezone.TimeZoneProviderSuggestion.class); - String failureCause = in.readString8(); - return new TimeZoneProviderEvent( - type, creationElapsedMillis, suggestion, failureCause); - } - - @Override - public TimeZoneProviderEvent[] newArray(int size) { - return new TimeZoneProviderEvent[size]; - } - }; + /** + * Returns the status of the time zone provider. Populated when {@link #getType()} is {@link + * #EVENT_TYPE_UNCERTAIN} or {@link #EVENT_TYPE_SUGGESTION}. + */ + @Nullable + public TimeZoneProviderStatus getTimeZoneProviderStatus() { + return mTimeZoneProviderStatus; + } + + public static final @NonNull Creator CREATOR = new Creator<>() { + @Override + public TimeZoneProviderEvent createFromParcel(Parcel in) { + int type = in.readInt(); + long creationElapsedMillis = in.readLong(); + TimeZoneProviderSuggestion suggestion = in.readParcelable( + getClass().getClassLoader(), TimeZoneProviderSuggestion.class); + String failureCause = in.readString8(); + TimeZoneProviderStatus status = in.readParcelable( + getClass().getClassLoader(), TimeZoneProviderStatus.class); + return new TimeZoneProviderEvent( + type, creationElapsedMillis, suggestion, failureCause, status); + } + + @Override + public TimeZoneProviderEvent[] newArray(int size) { + return new TimeZoneProviderEvent[size]; + } + }; @Override public int describeContents() { @@ -164,6 +187,7 @@ public final class TimeZoneProviderEvent implements Parcelable { parcel.writeLong(mCreationElapsedMillis); parcel.writeParcelable(mSuggestion, 0); parcel.writeString8(mFailureCause); + parcel.writeParcelable(mTimeZoneProviderStatus, 0); } @Override @@ -173,14 +197,17 @@ public final class TimeZoneProviderEvent implements Parcelable { + ", mCreationElapsedMillis=" + Duration.ofMillis(mCreationElapsedMillis).toString() + ", mSuggestion=" + mSuggestion + ", mFailureCause=" + mFailureCause + + ", mTimeZoneProviderStatus=" + mTimeZoneProviderStatus + '}'; } /** * Similar to {@link #equals} except this methods checks for equivalence, not equality. - * i.e. two {@link #EVENT_TYPE_UNCERTAIN} and {@link #EVENT_TYPE_PERMANENT_FAILURE} events are - * always equivalent, two {@link #EVENT_TYPE_SUGGESTION} events are equivalent if they suggest - * the same time zones. + * i.e. two {@link #EVENT_TYPE_SUGGESTION} events are equivalent if they suggest + * the same time zones and have the same provider status, two {@link #EVENT_TYPE_UNCERTAIN} + * events are equivalent if they have the same provider status, and {@link + * #EVENT_TYPE_PERMANENT_FAILURE} events are always equivalent (the nature of the failure is not + * considered). */ @SuppressWarnings("ReferenceEquality") public boolean isEquivalentTo(@Nullable TimeZoneProviderEvent other) { @@ -191,9 +218,10 @@ public final class TimeZoneProviderEvent implements Parcelable { return false; } if (mType == EVENT_TYPE_SUGGESTION) { - return mSuggestion.isEquivalentTo(other.getSuggestion()); + return mSuggestion.isEquivalentTo(other.mSuggestion) + && Objects.equals(mTimeZoneProviderStatus, other.mTimeZoneProviderStatus); } - return true; + return Objects.equals(mTimeZoneProviderStatus, other.mTimeZoneProviderStatus); } @Override @@ -208,11 +236,13 @@ public final class TimeZoneProviderEvent implements Parcelable { return mType == that.mType && mCreationElapsedMillis == that.mCreationElapsedMillis && Objects.equals(mSuggestion, that.mSuggestion) - && Objects.equals(mFailureCause, that.mFailureCause); + && Objects.equals(mFailureCause, that.mFailureCause) + && Objects.equals(mTimeZoneProviderStatus, that.mTimeZoneProviderStatus); } @Override public int hashCode() { - return Objects.hash(mType, mCreationElapsedMillis, mSuggestion, mFailureCause); + return Objects.hash(mType, mCreationElapsedMillis, mSuggestion, mFailureCause, + mTimeZoneProviderStatus); } } diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java index 0d215f6d56f1..cd4a30598a9b 100644 --- a/core/java/android/service/timezone/TimeZoneProviderService.java +++ b/core/java/android/service/timezone/TimeZoneProviderService.java @@ -203,6 +203,20 @@ public abstract class TimeZoneProviderService extends Service { * details. */ public final void reportSuggestion(@NonNull TimeZoneProviderSuggestion suggestion) { + reportSuggestion(suggestion, TimeZoneProviderStatus.UNKNOWN); + } + + /** + * Indicates a successful time zone detection. See {@link TimeZoneProviderSuggestion} for + * details. + * + * @param providerStatus provider status information that can influence detector service + * behavior and/or be reported via the device UI + * + * @hide + */ + public final void reportSuggestion(@NonNull TimeZoneProviderSuggestion suggestion, + @NonNull TimeZoneProviderStatus providerStatus) { Objects.requireNonNull(suggestion); mHandler.post(() -> { @@ -212,7 +226,7 @@ public abstract class TimeZoneProviderService extends Service { try { TimeZoneProviderEvent thisEvent = TimeZoneProviderEvent.createSuggestionEvent( - SystemClock.elapsedRealtime(), suggestion); + SystemClock.elapsedRealtime(), suggestion, providerStatus); if (shouldSendEvent(thisEvent)) { manager.onTimeZoneProviderEvent(thisEvent); mLastEventSent = thisEvent; @@ -231,6 +245,21 @@ public abstract class TimeZoneProviderService extends Service { * to a time zone. */ public final void reportUncertain() { + reportUncertain(TimeZoneProviderStatus.UNKNOWN); + } + + /** + * Indicates the time zone is not known because of an expected runtime state or error. + * + *

When the status changes then a certain or uncertain report must be made to move the + * detector service to the new status. + * + * @param providerStatus provider status information that can influence detector service + * behavior and/or be reported via the device UI + * + * @hide + */ + public final void reportUncertain(@NonNull TimeZoneProviderStatus providerStatus) { mHandler.post(() -> { synchronized (mLock) { ITimeZoneProviderManager manager = mManager; @@ -238,7 +267,7 @@ public abstract class TimeZoneProviderService extends Service { try { TimeZoneProviderEvent thisEvent = TimeZoneProviderEvent.createUncertainEvent( - SystemClock.elapsedRealtime()); + SystemClock.elapsedRealtime(), providerStatus); if (shouldSendEvent(thisEvent)) { manager.onTimeZoneProviderEvent(thisEvent); mLastEventSent = thisEvent; diff --git a/core/java/android/service/timezone/TimeZoneProviderStatus.aidl b/core/java/android/service/timezone/TimeZoneProviderStatus.aidl new file mode 100644 index 000000000000..91dc7e99fd9a --- /dev/null +++ b/core/java/android/service/timezone/TimeZoneProviderStatus.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2022, 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; + +/** + * @hide + */ +parcelable TimeZoneProviderStatus; diff --git a/core/java/android/service/timezone/TimeZoneProviderStatus.java b/core/java/android/service/timezone/TimeZoneProviderStatus.java new file mode 100644 index 000000000000..87d7843bacaa --- /dev/null +++ b/core/java/android/service/timezone/TimeZoneProviderStatus.java @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2022 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 android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Objects; + +/** + * Information about the status of a {@link TimeZoneProviderService}. + * + *

Not all status properties or status values will apply to all provider implementations. + * {@code _NOT_APPLICABLE} status can be used to indicate properties that have no meaning for a + * given implementation. + * + *

Time zone providers are expected to work in one of two ways: + *

    + *
  1. Location: Providers will determine location and then map that location to one or more + * time zone IDs.
  2. + *
  3. External signals: Providers could use indirect signals like country code + * and/or local offset / DST information provided to the device to infer a time zone, e.g. + * signals like MCC and NITZ for telephony devices, IP geo location, or DHCP information + * (RFC4833). The time zone ID could also be fed directly to the device by an external service. + *
  4. + *
+ * + *

The status properties are: + *

    + *
  • location detection - for location-based providers, the status of the location detection + * mechanism
  • + *
  • connectivity - connectivity can influence providers directly, for example if they use + * a networked service to map location to time zone ID, or use geo IP, or indirectly for + * location detection (e.g. for the network location provider.
  • + *
  • time zone resolution - the status related to determining a time zone ID or using a + * detected time zone ID. For example, a networked service may be reachable (i.e. connectivity + * is working) but the service could return errors, a time zone ID detected may not be usable + * for a device because of TZDB version skew, or external indirect signals may available but + * do not match the properties of a known time zone ID.
  • + *
+ * + * @hide + */ +public final class TimeZoneProviderStatus implements Parcelable { + + /** + * A status code related to a dependency a provider may have. + * + * @hide + */ + @IntDef(prefix = "DEPENDENCY_STATUS_", value = { + DEPENDENCY_STATUS_UNKNOWN, + DEPENDENCY_STATUS_NOT_APPLICABLE, + DEPENDENCY_STATUS_WORKING, + DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE, + DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT, + DEPENDENCY_STATUS_DEGRADED_BY_SETTINGS, + DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS, + }) + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.SOURCE) + public @interface DependencyStatus {} + + /** The dependency's status is unknown. */ + public static final @DependencyStatus int DEPENDENCY_STATUS_UNKNOWN = 0; + + /** The dependency is not used by the provider's implementation. */ + public static final @DependencyStatus int DEPENDENCY_STATUS_NOT_APPLICABLE = 1; + + /** The dependency is applicable and working well. */ + public static final @DependencyStatus int DEPENDENCY_STATUS_WORKING = 2; + + /** + * The dependency is used but is temporarily unavailable, e.g. connectivity has been lost for an + * unpredictable amount of time. + * + *

This status is considered normal is may be entered many times a day. + */ + public static final @DependencyStatus int DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE = 3; + + /** + * The dependency is used by the provider but is blocked by the environment in a way that the + * provider has detected and is considered likely to persist for some time, e.g. connectivity + * has been lost due to boarding a plane. + * + *

This status is considered unusual and could be used by the system as a trigger to try + * other time zone providers / time zone detection mechanisms. The bar for using this status + * should therefore be set fairly high to avoid a device bringing up other providers or + * switching to a different detection mechanism that may provide a different suggestion. + */ + public static final @DependencyStatus int DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT = 4; + + /** + * The dependency is used by the provider but is running in a degraded mode due to the user's + * settings. A user can take action to improve this, e.g. by changing a setting. + * + *

This status could be used by the system as a trigger to try other time zone + * providers / time zone detection mechanisms. The user may be informed. + */ + public static final @DependencyStatus int DEPENDENCY_STATUS_DEGRADED_BY_SETTINGS = 5; + + /** + * The dependency is used by the provider but is completely blocked by the user's settings. + * A user can take action to correct this, e.g. by changing a setting. + * + *

This status could be used by the system as a trigger to try other time zone providers / + * time zone detection mechanisms. The user may be informed. + */ + public static final @DependencyStatus int DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS = 6; + + /** + * A status code related to an operation in a provider's detection algorithm. + * + * @hide + */ + @IntDef(prefix = "OPERATION_STATUS_", value = { + OPERATION_STATUS_UNKNOWN, + OPERATION_STATUS_NOT_APPLICABLE, + OPERATION_STATUS_WORKING, + OPERATION_STATUS_FAILED, + }) + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.SOURCE) + public @interface OperationStatus {} + + /** The operation's status is unknown. */ + public static final @OperationStatus int OPERATION_STATUS_UNKNOWN = 0; + + /** The operation is not used by the provider's implementation. */ + public static final @OperationStatus int OPERATION_STATUS_NOT_APPLICABLE = 1; + + /** The operation is applicable and working well. */ + public static final @OperationStatus int OPERATION_STATUS_WORKING = 2; + + /** The operation is applicable and failed. */ + public static final @OperationStatus int OPERATION_STATUS_FAILED = 3; + + /** + * An instance that provides no information about status. Effectively a "null" status. + */ + @NonNull + public static final TimeZoneProviderStatus UNKNOWN = new TimeZoneProviderStatus( + DEPENDENCY_STATUS_UNKNOWN, DEPENDENCY_STATUS_UNKNOWN, OPERATION_STATUS_UNKNOWN); + + private final @DependencyStatus int mLocationDetectionStatus; + private final @DependencyStatus int mConnectivityStatus; + private final @OperationStatus int mTimeZoneResolutionStatus; + + private TimeZoneProviderStatus( + @DependencyStatus int locationDetectionStatus, + @DependencyStatus int connectivityStatus, + @OperationStatus int timeZoneResolutionStatus) { + mLocationDetectionStatus = requireValidDependencyStatus(locationDetectionStatus); + mConnectivityStatus = requireValidDependencyStatus(connectivityStatus); + mTimeZoneResolutionStatus = requireValidOperationStatus(timeZoneResolutionStatus); + } + + /** + * Returns the status of the location detection dependencies used by the provider (where + * applicable). + */ + public @DependencyStatus int getLocationDetectionStatus() { + return mLocationDetectionStatus; + } + + /** + * Returns the status of the connectivity dependencies used by the provider (where applicable). + */ + public @DependencyStatus int getConnectivityStatus() { + return mConnectivityStatus; + } + + /** + * Returns the status of the time zone resolution operation used by the provider. + */ + public @OperationStatus int getTimeZoneResolutionStatus() { + return mTimeZoneResolutionStatus; + } + + @Override + public String toString() { + return "TimeZoneProviderStatus{" + + "mLocationDetectionStatus=" + mLocationDetectionStatus + + ", mConnectivityStatus=" + mConnectivityStatus + + ", mTimeZoneResolutionStatus=" + mTimeZoneResolutionStatus + + '}'; + } + + public static final @NonNull Creator CREATOR = new Creator<>() { + @Override + public TimeZoneProviderStatus createFromParcel(Parcel in) { + @DependencyStatus int locationDetectionStatus = in.readInt(); + @DependencyStatus int connectivityStatus = in.readInt(); + @OperationStatus int timeZoneResolutionStatus = in.readInt(); + return new TimeZoneProviderStatus( + locationDetectionStatus, connectivityStatus, timeZoneResolutionStatus); + } + + @Override + public TimeZoneProviderStatus[] newArray(int size) { + return new TimeZoneProviderStatus[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel parcel, int flags) { + parcel.writeInt(mLocationDetectionStatus); + parcel.writeInt(mConnectivityStatus); + parcel.writeInt(mTimeZoneResolutionStatus); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TimeZoneProviderStatus that = (TimeZoneProviderStatus) o; + return mLocationDetectionStatus == that.mLocationDetectionStatus + && mConnectivityStatus == that.mConnectivityStatus + && mTimeZoneResolutionStatus == that.mTimeZoneResolutionStatus; + } + + @Override + public int hashCode() { + return Objects.hash( + mLocationDetectionStatus, mConnectivityStatus, mTimeZoneResolutionStatus); + } + + /** A builder for {@link TimeZoneProviderStatus}. */ + public static final class Builder { + + private @DependencyStatus int mLocationDetectionStatus = DEPENDENCY_STATUS_UNKNOWN; + private @DependencyStatus int mConnectivityStatus = DEPENDENCY_STATUS_UNKNOWN; + private @OperationStatus int mTimeZoneResolutionStatus = OPERATION_STATUS_UNKNOWN; + + /** + * Creates a new builder instance. At creation time all status properties are set to + * their "UNKNOWN" value. + */ + public Builder() { + } + + /** + * @hide + */ + public Builder(TimeZoneProviderStatus toCopy) { + mLocationDetectionStatus = toCopy.mLocationDetectionStatus; + mConnectivityStatus = toCopy.mConnectivityStatus; + mTimeZoneResolutionStatus = toCopy.mTimeZoneResolutionStatus; + } + + /** + * Sets the status of the provider's location detection dependency (where applicable). + * See the {@code DEPENDENCY_STATUS_} constants for more information. + */ + @NonNull + public Builder setLocationDetectionStatus(@DependencyStatus int locationDetectionStatus) { + mLocationDetectionStatus = locationDetectionStatus; + return this; + } + + /** + * Sets the status of the provider's connectivity dependency (where applicable). + * See the {@code DEPENDENCY_STATUS_} constants for more information. + */ + @NonNull + public Builder setConnectivityStatus(@DependencyStatus int connectivityStatus) { + mConnectivityStatus = connectivityStatus; + return this; + } + + /** + * Sets the status of the provider's time zone resolution operation. + * See the {@code OPERATION_STATUS_} constants for more information. + */ + @NonNull + public Builder setTimeZoneResolutionStatus(@OperationStatus int timeZoneResolutionStatus) { + mTimeZoneResolutionStatus = timeZoneResolutionStatus; + return this; + } + + /** + * Builds a {@link TimeZoneProviderStatus} instance. + */ + @NonNull + public TimeZoneProviderStatus build() { + return new TimeZoneProviderStatus( + mLocationDetectionStatus, mConnectivityStatus, mTimeZoneResolutionStatus); + } + } + + private @OperationStatus int requireValidOperationStatus(@OperationStatus int operationStatus) { + if (operationStatus < OPERATION_STATUS_UNKNOWN + || operationStatus > OPERATION_STATUS_FAILED) { + throw new IllegalArgumentException(Integer.toString(operationStatus)); + } + return operationStatus; + } + + private static @DependencyStatus int requireValidDependencyStatus( + @DependencyStatus int dependencyStatus) { + if (dependencyStatus < DEPENDENCY_STATUS_UNKNOWN + || dependencyStatus > DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS) { + throw new IllegalArgumentException(Integer.toString(dependencyStatus)); + } + return dependencyStatus; + } +} diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java b/core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java index c8de190b30b0..ab63f1475f83 100644 --- a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java +++ b/core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java @@ -17,6 +17,9 @@ package android.service.timezone; import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; +import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_WORKING; +import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_FAILED; +import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_WORKING; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -33,6 +36,32 @@ public class TimeZoneProviderEventTest { @Test public void isEquivalentToAndEquals() { + long creationElapsedMillis = 1111L; + TimeZoneProviderEvent failEvent = + TimeZoneProviderEvent.createPermanentFailureEvent(creationElapsedMillis, "one"); + TimeZoneProviderStatus providerStatus = TimeZoneProviderStatus.UNKNOWN; + + TimeZoneProviderEvent uncertainEvent = + TimeZoneProviderEvent.createUncertainEvent(creationElapsedMillis, providerStatus); + TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder() + .setElapsedRealtimeMillis(creationElapsedMillis) + .setTimeZoneIds(Collections.singletonList("Europe/London")) + .build(); + TimeZoneProviderEvent suggestionEvent = TimeZoneProviderEvent.createSuggestionEvent( + creationElapsedMillis, suggestion, providerStatus); + + assertNotEquals(failEvent, uncertainEvent); + assertNotEquivalentTo(failEvent, uncertainEvent); + + assertNotEquals(failEvent, suggestionEvent); + assertNotEquivalentTo(failEvent, suggestionEvent); + + assertNotEquals(uncertainEvent, suggestionEvent); + assertNotEquivalentTo(uncertainEvent, suggestionEvent); + } + + @Test + public void isEquivalentToAndEquals_permanentFailure() { TimeZoneProviderEvent fail1v1 = TimeZoneProviderEvent.createPermanentFailureEvent(1111L, "one"); assertEquals(fail1v1, fail1v1); @@ -51,44 +80,79 @@ public class TimeZoneProviderEventTest { assertNotEquals(fail1v1, fail2); assertIsEquivalentTo(fail1v1, fail2); } + } + + @Test + public void isEquivalentToAndEquals_uncertain() { + TimeZoneProviderStatus status1 = new TimeZoneProviderStatus.Builder() + .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING) + .setConnectivityStatus(DEPENDENCY_STATUS_WORKING) + .setTimeZoneResolutionStatus(OPERATION_STATUS_WORKING) + .build(); + TimeZoneProviderStatus status2 = new TimeZoneProviderStatus.Builder() + .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING) + .setConnectivityStatus(DEPENDENCY_STATUS_WORKING) + .setTimeZoneResolutionStatus(OPERATION_STATUS_FAILED) + .build(); - TimeZoneProviderEvent uncertain1v1 = TimeZoneProviderEvent.createUncertainEvent(1111L); + TimeZoneProviderEvent uncertain1v1 = + TimeZoneProviderEvent.createUncertainEvent(1111L, status1); assertEquals(uncertain1v1, uncertain1v1); assertIsEquivalentTo(uncertain1v1, uncertain1v1); assertNotEquals(uncertain1v1, null); assertNotEquivalentTo(uncertain1v1, null); { - TimeZoneProviderEvent uncertain1v2 = TimeZoneProviderEvent.createUncertainEvent(1111L); + TimeZoneProviderEvent uncertain1v2 = + TimeZoneProviderEvent.createUncertainEvent(1111L, status1); assertEquals(uncertain1v1, uncertain1v2); assertIsEquivalentTo(uncertain1v1, uncertain1v2); - TimeZoneProviderEvent uncertain2 = TimeZoneProviderEvent.createUncertainEvent(2222L); + TimeZoneProviderEvent uncertain2 = + TimeZoneProviderEvent.createUncertainEvent(2222L, status1); assertNotEquals(uncertain1v1, uncertain2); assertIsEquivalentTo(uncertain1v1, uncertain2); + + TimeZoneProviderEvent uncertain3 = + TimeZoneProviderEvent.createUncertainEvent(1111L, status2); + assertNotEquals(uncertain1v1, uncertain3); + assertNotEquivalentTo(uncertain1v1, uncertain3); } + } + @Test + public void isEquivalentToAndEquals_suggestion() { + TimeZoneProviderStatus status1 = new TimeZoneProviderStatus.Builder() + .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING) + .setConnectivityStatus(DEPENDENCY_STATUS_WORKING) + .setTimeZoneResolutionStatus(OPERATION_STATUS_WORKING) + .build(); + TimeZoneProviderStatus status2 = new TimeZoneProviderStatus.Builder() + .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING) + .setConnectivityStatus(DEPENDENCY_STATUS_WORKING) + .setTimeZoneResolutionStatus(OPERATION_STATUS_FAILED) + .build(); TimeZoneProviderSuggestion suggestion1 = new TimeZoneProviderSuggestion.Builder() .setElapsedRealtimeMillis(1111L) .setTimeZoneIds(Collections.singletonList("Europe/London")) .build(); TimeZoneProviderEvent certain1v1 = - TimeZoneProviderEvent.createSuggestionEvent(1111L, suggestion1); + TimeZoneProviderEvent.createSuggestionEvent(1111L, suggestion1, status1); assertEquals(certain1v1, certain1v1); assertIsEquivalentTo(certain1v1, certain1v1); assertNotEquals(certain1v1, null); assertNotEquivalentTo(certain1v1, null); { - // Same suggestion, same time. + // Same time, suggestion, and status. TimeZoneProviderEvent certain1v2 = - TimeZoneProviderEvent.createSuggestionEvent(1111L, suggestion1); + TimeZoneProviderEvent.createSuggestionEvent(1111L, suggestion1, status1); assertEquals(certain1v1, certain1v2); assertIsEquivalentTo(certain1v1, certain1v2); - // Same suggestion, different time. + // Different time, same suggestion and status. TimeZoneProviderEvent certain1v3 = - TimeZoneProviderEvent.createSuggestionEvent(2222L, suggestion1); + TimeZoneProviderEvent.createSuggestionEvent(2222L, suggestion1, status1); assertNotEquals(certain1v1, certain1v3); assertIsEquivalentTo(certain1v1, certain1v3); @@ -100,7 +164,7 @@ public class TimeZoneProviderEventTest { assertNotEquals(suggestion1, suggestion2); TimeZoneProviderSuggestionTest.assertIsEquivalentTo(suggestion1, suggestion2); TimeZoneProviderEvent certain2 = - TimeZoneProviderEvent.createSuggestionEvent(2222L, suggestion2); + TimeZoneProviderEvent.createSuggestionEvent(2222L, suggestion2, status1); assertNotEquals(certain1v1, certain2); assertIsEquivalentTo(certain1v1, certain2); @@ -109,16 +173,15 @@ public class TimeZoneProviderEventTest { .setTimeZoneIds(Collections.singletonList("Europe/Paris")) .build(); TimeZoneProviderEvent certain3 = - TimeZoneProviderEvent.createSuggestionEvent(2222L, suggestion3); + TimeZoneProviderEvent.createSuggestionEvent(2222L, suggestion3, status1); assertNotEquals(certain1v1, certain3); assertNotEquivalentTo(certain1v1, certain3); - } - assertNotEquals(fail1v1, uncertain1v1); - assertNotEquivalentTo(fail1v1, uncertain1v1); - - assertNotEquals(fail1v1, certain1v1); - assertNotEquivalentTo(fail1v1, certain1v1); + TimeZoneProviderEvent certain4 = + TimeZoneProviderEvent.createSuggestionEvent(2222L, suggestion1, status2); + assertNotEquals(certain1v1, certain4); + assertNotEquivalentTo(certain1v1, certain4); + } } @Test @@ -130,7 +193,8 @@ public class TimeZoneProviderEventTest { @Test public void testParcelable_uncertain() { - TimeZoneProviderEvent event = TimeZoneProviderEvent.createUncertainEvent(1111L); + TimeZoneProviderEvent event = TimeZoneProviderEvent.createUncertainEvent( + 1111L, TimeZoneProviderStatus.UNKNOWN); assertRoundTripParcelable(event); } @@ -139,8 +203,8 @@ public class TimeZoneProviderEventTest { TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder() .setTimeZoneIds(Arrays.asList("Europe/London", "Europe/Paris")) .build(); - TimeZoneProviderEvent event = - TimeZoneProviderEvent.createSuggestionEvent(1111L, suggestion); + TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent( + 1111L, suggestion, TimeZoneProviderStatus.UNKNOWN); assertRoundTripParcelable(event); } diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java b/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java new file mode 100644 index 000000000000..d61c33c935db --- /dev/null +++ b/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java @@ -0,0 +1,110 @@ +/* + * 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 android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; +import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT; +import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_WORKING; +import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_FAILED; +import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_WORKING; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertThrows; + +import org.junit.Test; + +public class TimeZoneProviderStatusTest { + + @Test + public void testStatusValidation() { + TimeZoneProviderStatus status = new TimeZoneProviderStatus.Builder() + .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING) + .setConnectivityStatus(DEPENDENCY_STATUS_WORKING) + .setTimeZoneResolutionStatus(DEPENDENCY_STATUS_WORKING) + .build(); + + assertThrows(IllegalArgumentException.class, + () -> new TimeZoneProviderStatus.Builder(status) + .setLocationDetectionStatus(-1) + .build()); + assertThrows(IllegalArgumentException.class, + () -> new TimeZoneProviderStatus.Builder(status) + .setConnectivityStatus(-1) + .build()); + assertThrows(IllegalArgumentException.class, + () -> new TimeZoneProviderStatus.Builder(status) + .setTimeZoneResolutionStatus(-1) + .build()); + } + + @Test + public void testEqualsAndHashcode() { + TimeZoneProviderStatus status1_1 = new TimeZoneProviderStatus.Builder() + .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING) + .setConnectivityStatus(DEPENDENCY_STATUS_WORKING) + .setTimeZoneResolutionStatus(OPERATION_STATUS_WORKING) + .build(); + assertEqualsAndHashcode(status1_1, status1_1); + assertNotEquals(status1_1, null); + + { + TimeZoneProviderStatus status1_2 = + new TimeZoneProviderStatus.Builder(status1_1).build(); + assertEqualsAndHashcode(status1_1, status1_2); + assertNotSame(status1_1, status1_2); + } + + { + TimeZoneProviderStatus status2 = new TimeZoneProviderStatus.Builder(status1_1) + .setLocationDetectionStatus(DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT) + .build(); + assertNotEquals(status1_1, status2); + } + + { + TimeZoneProviderStatus status2 = new TimeZoneProviderStatus.Builder(status1_1) + .setConnectivityStatus(DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT) + .build(); + assertNotEquals(status1_1, status2); + } + + { + TimeZoneProviderStatus status2 = new TimeZoneProviderStatus.Builder(status1_1) + .setTimeZoneResolutionStatus(OPERATION_STATUS_FAILED) + .build(); + assertNotEquals(status1_1, status2); + } + } + + private static void assertEqualsAndHashcode(Object one, Object two) { + assertEquals(one, two); + assertEquals(two, one); + assertEquals(one.hashCode(), two.hashCode()); + } + + @Test + public void testParcelable() { + TimeZoneProviderStatus status = new TimeZoneProviderStatus.Builder() + .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING) + .setConnectivityStatus(DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT) + .setTimeZoneResolutionStatus(OPERATION_STATUS_FAILED) + .build(); + assertRoundTripParcelable(status); + } +} diff --git a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java index ff0529f35057..8a6f92750501 100644 --- a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java +++ b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java @@ -16,10 +16,13 @@ package com.android.server.timezonedetector.location; +import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_FAILED; + import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.infoLog; import android.annotation.NonNull; import android.service.timezone.TimeZoneProviderEvent; +import android.service.timezone.TimeZoneProviderStatus; import com.android.i18n.timezone.ZoneInfoDb; @@ -53,7 +56,12 @@ 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(event.getCreationElapsedMillis()); + TimeZoneProviderStatus providerStatus = new TimeZoneProviderStatus.Builder( + event.getTimeZoneProviderStatus()) + .setTimeZoneResolutionStatus(OPERATION_STATUS_FAILED) + .build(); + return TimeZoneProviderEvent.createUncertainEvent( + event.getCreationElapsedMillis(), providerStatus); } return event; diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java index 52e9d3a06fe2..34d008201bb2 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java @@ -17,6 +17,7 @@ package com.android.server.timezonedetector.location; import android.service.timezone.TimeZoneProviderEvent; +import android.service.timezone.TimeZoneProviderStatus; /** * Fake implementation of {@link TimeZoneProviderEventPreProcessor} which assumes that all events @@ -31,7 +32,8 @@ public final class FakeTimeZoneProviderEventPreProcessor public TimeZoneProviderEvent preProcess(TimeZoneProviderEvent timeZoneProviderEvent) { if (mIsUncertain) { return TimeZoneProviderEvent.createUncertainEvent( - timeZoneProviderEvent.getCreationElapsedMillis()); + timeZoneProviderEvent.getCreationElapsedMillis(), + TimeZoneProviderStatus.UNKNOWN); } return timeZoneProviderEvent; } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java index 0257ce0fe7b9..ed426cdc9f7e 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java @@ -15,6 +15,12 @@ */ package com.android.server.timezonedetector.location; +import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_NOT_APPLICABLE; +import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE; +import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_WORKING; +import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_UNKNOWN; +import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_WORKING; + import static com.android.server.timezonedetector.ConfigurationInternal.DETECTION_MODE_MANUAL; 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; @@ -48,6 +54,7 @@ import android.annotation.Nullable; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.service.timezone.TimeZoneProviderEvent; +import android.service.timezone.TimeZoneProviderStatus; import android.service.timezone.TimeZoneProviderSuggestion; import android.util.IndentingPrintWriter; @@ -78,8 +85,15 @@ public class LocationTimeZoneProviderControllerTest { createSuggestionEvent(asList("Europe/London")); private static final TimeZoneProviderEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2 = createSuggestionEvent(asList("Europe/Paris")); + private static final TimeZoneProviderStatus UNCERTAIN_PROVIDER_STATUS = + new TimeZoneProviderStatus.Builder() + .setLocationDetectionStatus(DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE) + .setConnectivityStatus(DEPENDENCY_STATUS_WORKING) + .setTimeZoneResolutionStatus(OPERATION_STATUS_UNKNOWN) + .build(); private static final TimeZoneProviderEvent USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT = - TimeZoneProviderEvent.createUncertainEvent(ARBITRARY_TIME_MILLIS); + TimeZoneProviderEvent.createUncertainEvent( + ARBITRARY_TIME_MILLIS, UNCERTAIN_PROVIDER_STATUS); private static final TimeZoneProviderEvent USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT = TimeZoneProviderEvent.createPermanentFailureEvent(ARBITRARY_TIME_MILLIS, "Test"); @@ -1390,12 +1404,17 @@ public class LocationTimeZoneProviderControllerTest { } private static TimeZoneProviderEvent createSuggestionEvent(@NonNull List timeZoneIds) { + TimeZoneProviderStatus providerStatus = new TimeZoneProviderStatus.Builder() + .setLocationDetectionStatus(DEPENDENCY_STATUS_NOT_APPLICABLE) + .setConnectivityStatus(DEPENDENCY_STATUS_NOT_APPLICABLE) + .setTimeZoneResolutionStatus(OPERATION_STATUS_WORKING) + .build(); + TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder() + .setElapsedRealtimeMillis(ARBITRARY_TIME_MILLIS) + .setTimeZoneIds(timeZoneIds) + .build(); return TimeZoneProviderEvent.createSuggestionEvent( - ARBITRARY_TIME_MILLIS, - new TimeZoneProviderSuggestion.Builder() - .setElapsedRealtimeMillis(ARBITRARY_TIME_MILLIS) - .setTimeZoneIds(timeZoneIds) - .build()); + ARBITRARY_TIME_MILLIS, suggestion, providerStatus); } private static void assertControllerState(LocationTimeZoneProviderController controller, diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java index cb2905d2266a..8429fa4d18d1 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java @@ -33,6 +33,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.platform.test.annotations.Presubmit; import android.service.timezone.TimeZoneProviderEvent; +import android.service.timezone.TimeZoneProviderStatus; import android.service.timezone.TimeZoneProviderSuggestion; import android.util.IndentingPrintWriter; @@ -120,8 +121,9 @@ public class LocationTimeZoneProviderTest { .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS) .setTimeZoneIds(Arrays.asList("Europe/London")) .build(); + TimeZoneProviderStatus providerStatus = TimeZoneProviderStatus.UNKNOWN; TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent( - ARBITRARY_ELAPSED_REALTIME_MILLIS, suggestion); + ARBITRARY_ELAPSED_REALTIME_MILLIS, suggestion, providerStatus); provider.simulateProviderEventReceived(event); currentState = assertAndReturnProviderState( @@ -133,7 +135,8 @@ public class LocationTimeZoneProviderTest { mProviderListener.assertProviderChangeReported(PROVIDER_STATE_STARTED_CERTAIN); // Simulate an uncertain event being received. - event = TimeZoneProviderEvent.createUncertainEvent(ARBITRARY_ELAPSED_REALTIME_MILLIS); + event = TimeZoneProviderEvent.createUncertainEvent(ARBITRARY_ELAPSED_REALTIME_MILLIS, + TimeZoneProviderStatus.UNKNOWN); provider.simulateProviderEventReceived(event); currentState = assertAndReturnProviderState( @@ -193,12 +196,13 @@ public class LocationTimeZoneProviderTest { .setTimeZoneIds(Arrays.asList("Europe/London")) .build(); TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent( - ARBITRARY_ELAPSED_REALTIME_MILLIS, suggestion); + ARBITRARY_ELAPSED_REALTIME_MILLIS, suggestion, TimeZoneProviderStatus.UNKNOWN); provider.simulateProviderEventReceived(event); provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_CERTAIN); // Simulate an uncertain event being received. - event = TimeZoneProviderEvent.createUncertainEvent(ARBITRARY_ELAPSED_REALTIME_MILLIS); + event = TimeZoneProviderEvent.createUncertainEvent(ARBITRARY_ELAPSED_REALTIME_MILLIS, + TimeZoneProviderStatus.UNKNOWN); provider.simulateProviderEventReceived(event); provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_UNCERTAIN); @@ -235,8 +239,9 @@ public class LocationTimeZoneProviderTest { .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS) .setTimeZoneIds(invalidTimeZoneIds) .build(); + TimeZoneProviderStatus providerStatus = TimeZoneProviderStatus.UNKNOWN; TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent( - ARBITRARY_ELAPSED_REALTIME_MILLIS, invalidIdSuggestion); + ARBITRARY_ELAPSED_REALTIME_MILLIS, invalidIdSuggestion, providerStatus); provider.simulateProviderEventReceived(event); provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_UNCERTAIN); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java index ab4fe2938bcf..c4786043cc29 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java @@ -16,10 +16,15 @@ package com.android.server.timezonedetector.location; +import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_WORKING; +import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_FAILED; +import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_WORKING; + import static com.google.common.truth.Truth.assertWithMessage; import android.platform.test.annotations.Presubmit; import android.service.timezone.TimeZoneProviderEvent; +import android.service.timezone.TimeZoneProviderStatus; import android.service.timezone.TimeZoneProviderSuggestion; import org.junit.Test; @@ -54,8 +59,14 @@ public class ZoneInfoDbTimeZoneProviderEventPreProcessorTest { for (String timeZone : nonExistingTimeZones) { TimeZoneProviderEvent event = timeZoneProviderEvent(timeZone); + TimeZoneProviderStatus expectedProviderStatus = + new TimeZoneProviderStatus.Builder(event.getTimeZoneProviderStatus()) + .setTimeZoneResolutionStatus(OPERATION_STATUS_FAILED) + .build(); + TimeZoneProviderEvent expectedResultEvent = - TimeZoneProviderEvent.createUncertainEvent(event.getCreationElapsedMillis()); + TimeZoneProviderEvent.createUncertainEvent( + event.getCreationElapsedMillis(), expectedProviderStatus); assertWithMessage(timeZone + " is not a valid time zone") .that(mPreProcessor.preProcess(event)) .isEqualTo(expectedResultEvent); @@ -63,12 +74,17 @@ public class ZoneInfoDbTimeZoneProviderEventPreProcessorTest { } private static TimeZoneProviderEvent timeZoneProviderEvent(String... timeZoneIds) { + TimeZoneProviderStatus providerStatus = new TimeZoneProviderStatus.Builder() + .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING) + .setConnectivityStatus(DEPENDENCY_STATUS_WORKING) + .setTimeZoneResolutionStatus(OPERATION_STATUS_WORKING) + .build(); + TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder() + .setTimeZoneIds(Arrays.asList(timeZoneIds)) + .setElapsedRealtimeMillis(ARBITRARY_TIME_MILLIS) + .build(); return TimeZoneProviderEvent.createSuggestionEvent( - ARBITRARY_TIME_MILLIS, - new TimeZoneProviderSuggestion.Builder() - .setTimeZoneIds(Arrays.asList(timeZoneIds)) - .setElapsedRealtimeMillis(ARBITRARY_TIME_MILLIS) - .build()); + ARBITRARY_TIME_MILLIS, suggestion, providerStatus); } } -- cgit v1.2.3-59-g8ed1b