summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/time/DetectorStatusTypes.java227
-rw-r--r--core/java/android/app/time/LocationTimeZoneAlgorithmStatus.aidl19
-rw-r--r--core/java/android/app/time/LocationTimeZoneAlgorithmStatus.java363
-rw-r--r--core/java/android/app/time/TelephonyTimeZoneAlgorithmStatus.aidl19
-rw-r--r--core/java/android/app/time/TelephonyTimeZoneAlgorithmStatus.java96
-rw-r--r--core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java66
-rw-r--r--core/java/android/app/time/TimeZoneDetectorStatus.aidl19
-rw-r--r--core/java/android/app/time/TimeZoneDetectorStatus.java124
-rw-r--r--core/java/android/app/timezonedetector/TimeZoneDetector.java4
-rw-r--r--core/proto/android/app/location_time_zone_manager.proto2
-rw-r--r--core/proto/android/app/time_zone_detector.proto29
-rw-r--r--core/tests/coretests/src/android/app/time/DetectorStatusTypesTest.java103
-rw-r--r--core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java210
-rw-r--r--core/tests/coretests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java68
-rw-r--r--core/tests/coretests/src/android/app/time/TimeZoneDetectorStatusTest.java105
-rw-r--r--services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java94
-rw-r--r--services/core/java/com/android/server/timezonedetector/LocationAlgorithmEvent.java194
-rw-r--r--services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java12
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java8
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java9
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java7
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java52
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java7
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java248
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java18
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java66
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java33
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java215
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerCallbackImpl.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java46
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java175
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java531
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java286
37 files changed, 2883 insertions, 679 deletions
diff --git a/core/java/android/app/time/DetectorStatusTypes.java b/core/java/android/app/time/DetectorStatusTypes.java
new file mode 100644
index 000000000000..3643fc9a7d86
--- /dev/null
+++ b/core/java/android/app/time/DetectorStatusTypes.java
@@ -0,0 +1,227 @@
+/*
+ * 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.app.time;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A set of constants that can relate to time or time zone detector status.
+ *
+ * <ul>
+ * <li>Detector status - the status of the overall detector.</li>
+ * <li>Detection algorithm status - the status of an algorithm that a detector can use.
+ * Each detector is expected to have one or more known algorithms to detect its chosen property,
+ * e.g. for time zone devices can have a "location" detection algorithm, where the device's
+ * location is used to detect the time zone.</li>
+ * </ul>
+ *
+ * @hide
+ */
+public final class DetectorStatusTypes {
+
+ /** A status code for a detector. */
+ @IntDef(prefix = "DETECTOR_STATUS_", value = {
+ DETECTOR_STATUS_UNKNOWN,
+ DETECTOR_STATUS_NOT_SUPPORTED,
+ DETECTOR_STATUS_NOT_RUNNING,
+ DETECTOR_STATUS_RUNNING,
+ })
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DetectorStatus {}
+
+ /**
+ * The detector status is unknown. Expected only for use as a placeholder before the actual
+ * status is known.
+ */
+ public static final @DetectorStatus int DETECTOR_STATUS_UNKNOWN = 0;
+
+ /** The detector is not supported on this device. */
+ public static final @DetectorStatus int DETECTOR_STATUS_NOT_SUPPORTED = 1;
+
+ /** The detector is supported but is not running. */
+ public static final @DetectorStatus int DETECTOR_STATUS_NOT_RUNNING = 2;
+
+ /** The detector is supported and is running. */
+ public static final @DetectorStatus int DETECTOR_STATUS_RUNNING = 3;
+
+ private DetectorStatusTypes() {}
+
+ /**
+ * A status code for a detection algorithm.
+ */
+ @IntDef(prefix = "DETECTION_ALGORITHM_STATUS_", value = {
+ DETECTION_ALGORITHM_STATUS_UNKNOWN,
+ DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED,
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING,
+ DETECTION_ALGORITHM_STATUS_RUNNING,
+ })
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DetectionAlgorithmStatus {}
+
+ /**
+ * The detection algorithm status is unknown. Expected only for use as a placeholder before the
+ * actual status is known.
+ */
+ public static final @DetectionAlgorithmStatus int DETECTION_ALGORITHM_STATUS_UNKNOWN = 0;
+
+ /** The detection algorithm is not supported on this device. */
+ public static final @DetectionAlgorithmStatus int DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED = 1;
+
+ /** The detection algorithm supported but is not running. */
+ public static final @DetectionAlgorithmStatus int DETECTION_ALGORITHM_STATUS_NOT_RUNNING = 2;
+
+ /** The detection algorithm supported and is running. */
+ public static final @DetectionAlgorithmStatus int DETECTION_ALGORITHM_STATUS_RUNNING = 3;
+
+ /**
+ * Validates the supplied value is one of the known {@code DETECTOR_STATUS_} constants and
+ * returns it if it is valid. {@link #DETECTOR_STATUS_UNKNOWN} is considered valid.
+ *
+ * @throws IllegalArgumentException if the value is not recognized
+ */
+ public static @DetectorStatus int requireValidDetectorStatus(
+ @DetectorStatus int detectorStatus) {
+ if (detectorStatus < DETECTOR_STATUS_UNKNOWN || detectorStatus > DETECTOR_STATUS_RUNNING) {
+ throw new IllegalArgumentException("Invalid detector status: " + detectorStatus);
+ }
+ return detectorStatus;
+ }
+
+ /**
+ * Returns a string for each {@code DETECTOR_STATUS_} constant. See also
+ * {@link #detectorStatusFromString(String)}.
+ *
+ * @throws IllegalArgumentException if the value is not recognized
+ */
+ @NonNull
+ public static String detectorStatusToString(@DetectorStatus int detectorStatus) {
+ switch (detectorStatus) {
+ case DETECTOR_STATUS_UNKNOWN:
+ return "UNKNOWN";
+ case DETECTOR_STATUS_NOT_SUPPORTED:
+ return "NOT_SUPPORTED";
+ case DETECTOR_STATUS_NOT_RUNNING:
+ return "NOT_RUNNING";
+ case DETECTOR_STATUS_RUNNING:
+ return "RUNNING";
+ default:
+ throw new IllegalArgumentException("Unknown status: " + detectorStatus);
+ }
+ }
+
+ /**
+ * Returns {@code DETECTOR_STATUS_} constant value from a string. See also
+ * {@link #detectorStatusToString(int)}.
+ *
+ * @throws IllegalArgumentException if the value is not recognized or is invalid
+ */
+ public static @DetectorStatus int detectorStatusFromString(
+ @Nullable String detectorStatusString) {
+ if (TextUtils.isEmpty(detectorStatusString)) {
+ throw new IllegalArgumentException("Empty status: " + detectorStatusString);
+ }
+
+ switch (detectorStatusString) {
+ case "UNKNOWN":
+ return DETECTOR_STATUS_UNKNOWN;
+ case "NOT_SUPPORTED":
+ return DETECTOR_STATUS_NOT_SUPPORTED;
+ case "NOT_RUNNING":
+ return DETECTOR_STATUS_NOT_RUNNING;
+ case "RUNNING":
+ return DETECTOR_STATUS_RUNNING;
+ default:
+ throw new IllegalArgumentException("Unknown status: " + detectorStatusString);
+ }
+ }
+
+ /**
+ * Validates the supplied value is one of the known {@code DETECTION_ALGORITHM_} constants and
+ * returns it if it is valid. {@link #DETECTION_ALGORITHM_STATUS_UNKNOWN} is considered valid.
+ *
+ * @throws IllegalArgumentException if the value is not recognized
+ */
+ public static @DetectionAlgorithmStatus int requireValidDetectionAlgorithmStatus(
+ @DetectionAlgorithmStatus int detectionAlgorithmStatus) {
+ if (detectionAlgorithmStatus < DETECTION_ALGORITHM_STATUS_UNKNOWN
+ || detectionAlgorithmStatus > DETECTION_ALGORITHM_STATUS_RUNNING) {
+ throw new IllegalArgumentException(
+ "Invalid detection algorithm: " + detectionAlgorithmStatus);
+ }
+ return detectionAlgorithmStatus;
+ }
+
+ /**
+ * Returns a string for each {@code DETECTION_ALGORITHM_} constant. See also
+ * {@link #detectionAlgorithmStatusFromString(String)}
+ *
+ * @throws IllegalArgumentException if the value is not recognized
+ */
+ @NonNull
+ public static String detectionAlgorithmStatusToString(
+ @DetectionAlgorithmStatus int detectorAlgorithmStatus) {
+ switch (detectorAlgorithmStatus) {
+ case DETECTION_ALGORITHM_STATUS_UNKNOWN:
+ return "UNKNOWN";
+ case DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED:
+ return "NOT_SUPPORTED";
+ case DETECTION_ALGORITHM_STATUS_NOT_RUNNING:
+ return "NOT_RUNNING";
+ case DETECTION_ALGORITHM_STATUS_RUNNING:
+ return "RUNNING";
+ default:
+ throw new IllegalArgumentException("Unknown status: " + detectorAlgorithmStatus);
+ }
+ }
+
+ /**
+ * Returns {@code DETECTION_ALGORITHM_} constant value from a string. See also
+ * {@link #detectionAlgorithmStatusToString(int)} (String)}
+ *
+ * @throws IllegalArgumentException if the value is not recognized or is invalid
+ */
+ public static @DetectionAlgorithmStatus int detectionAlgorithmStatusFromString(
+ @Nullable String detectorAlgorithmStatusString) {
+
+ if (TextUtils.isEmpty(detectorAlgorithmStatusString)) {
+ throw new IllegalArgumentException("Empty status: " + detectorAlgorithmStatusString);
+ }
+
+ switch (detectorAlgorithmStatusString) {
+ case "UNKNOWN":
+ return DETECTION_ALGORITHM_STATUS_UNKNOWN;
+ case "NOT_SUPPORTED":
+ return DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED;
+ case "NOT_RUNNING":
+ return DETECTION_ALGORITHM_STATUS_NOT_RUNNING;
+ case "RUNNING":
+ return DETECTION_ALGORITHM_STATUS_RUNNING;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown status: " + detectorAlgorithmStatusString);
+ }
+ }
+}
diff --git a/core/java/android/app/time/LocationTimeZoneAlgorithmStatus.aidl b/core/java/android/app/time/LocationTimeZoneAlgorithmStatus.aidl
new file mode 100644
index 000000000000..7184b123af1c
--- /dev/null
+++ b/core/java/android/app/time/LocationTimeZoneAlgorithmStatus.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.app.time;
+
+parcelable LocationTimeZoneAlgorithmStatus;
diff --git a/core/java/android/app/time/LocationTimeZoneAlgorithmStatus.java b/core/java/android/app/time/LocationTimeZoneAlgorithmStatus.java
new file mode 100644
index 000000000000..710b8c40cefe
--- /dev/null
+++ b/core/java/android/app/time/LocationTimeZoneAlgorithmStatus.java
@@ -0,0 +1,363 @@
+/*
+ * 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.app.time;
+
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_UNKNOWN;
+import static android.app.time.DetectorStatusTypes.detectionAlgorithmStatusFromString;
+import static android.app.time.DetectorStatusTypes.detectionAlgorithmStatusToString;
+import static android.app.time.DetectorStatusTypes.requireValidDetectionAlgorithmStatus;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.time.DetectorStatusTypes.DetectionAlgorithmStatus;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.timezone.TimeZoneProviderStatus;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Information about the status of the location-based time zone detection algorithm.
+ *
+ * @hide
+ */
+public final class LocationTimeZoneAlgorithmStatus implements Parcelable {
+
+ /**
+ * An enum that describes a location time zone provider's status.
+ *
+ * @hide
+ */
+ @IntDef(prefix = "PROVIDER_STATUS_", value = {
+ PROVIDER_STATUS_NOT_PRESENT,
+ PROVIDER_STATUS_NOT_READY,
+ PROVIDER_STATUS_IS_CERTAIN,
+ PROVIDER_STATUS_IS_UNCERTAIN,
+ })
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProviderStatus {}
+
+ /**
+ * Indicates a provider is not present because it has not been configured, the configuration
+ * is bad, or the provider has reported a permanent failure.
+ */
+ public static final @ProviderStatus int PROVIDER_STATUS_NOT_PRESENT = 1;
+
+ /**
+ * Indicates a provider has not reported it is certain or uncertain. This may be because it has
+ * just started running, or it has been stopped.
+ */
+ public static final @ProviderStatus int PROVIDER_STATUS_NOT_READY = 2;
+
+ /**
+ * Indicates a provider last reported it is certain.
+ */
+ public static final @ProviderStatus int PROVIDER_STATUS_IS_CERTAIN = 3;
+
+ /**
+ * Indicates a provider last reported it is uncertain.
+ */
+ public static final @ProviderStatus int PROVIDER_STATUS_IS_UNCERTAIN = 4;
+
+ /**
+ * An instance that provides no information about algorithm status because the algorithm has not
+ * yet reported. Effectively a "null" status placeholder.
+ */
+ @NonNull
+ public static final LocationTimeZoneAlgorithmStatus UNKNOWN =
+ new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_UNKNOWN,
+ PROVIDER_STATUS_NOT_READY, null, PROVIDER_STATUS_NOT_READY, null);
+
+ private final @DetectionAlgorithmStatus int mStatus;
+ private final @ProviderStatus int mPrimaryProviderStatus;
+ // May be populated when mPrimaryProviderReportedStatus == PROVIDER_STATUS_IS_CERTAIN
+ // or PROVIDER_STATUS_IS_UNCERTAIN
+ @Nullable private final TimeZoneProviderStatus mPrimaryProviderReportedStatus;
+
+ private final @ProviderStatus int mSecondaryProviderStatus;
+ // May be populated when mSecondaryProviderReportedStatus == PROVIDER_STATUS_IS_CERTAIN
+ // or PROVIDER_STATUS_IS_UNCERTAIN
+ @Nullable private final TimeZoneProviderStatus mSecondaryProviderReportedStatus;
+
+ public LocationTimeZoneAlgorithmStatus(
+ @DetectionAlgorithmStatus int status,
+ @ProviderStatus int primaryProviderStatus,
+ @Nullable TimeZoneProviderStatus primaryProviderReportedStatus,
+ @ProviderStatus int secondaryProviderStatus,
+ @Nullable TimeZoneProviderStatus secondaryProviderReportedStatus) {
+
+ mStatus = requireValidDetectionAlgorithmStatus(status);
+ mPrimaryProviderStatus = requireValidProviderStatus(primaryProviderStatus);
+ mPrimaryProviderReportedStatus = primaryProviderReportedStatus;
+ mSecondaryProviderStatus = requireValidProviderStatus(secondaryProviderStatus);
+ mSecondaryProviderReportedStatus = secondaryProviderReportedStatus;
+
+ boolean primaryProviderHasReported = hasProviderReported(primaryProviderStatus);
+ boolean primaryProviderReportedStatusPresent = primaryProviderReportedStatus != null;
+ if (!primaryProviderHasReported && primaryProviderReportedStatusPresent) {
+ throw new IllegalArgumentException(
+ "primaryProviderReportedStatus=" + primaryProviderReportedStatus
+ + ", primaryProviderStatus="
+ + providerStatusToString(primaryProviderStatus));
+ }
+
+ boolean secondaryProviderHasReported = hasProviderReported(secondaryProviderStatus);
+ boolean secondaryProviderReportedStatusPresent = secondaryProviderReportedStatus != null;
+ if (!secondaryProviderHasReported && secondaryProviderReportedStatusPresent) {
+ throw new IllegalArgumentException(
+ "secondaryProviderReportedStatus=" + secondaryProviderReportedStatus
+ + ", secondaryProviderStatus="
+ + providerStatusToString(secondaryProviderStatus));
+ }
+
+ // If the algorithm isn't running, providers can't report.
+ if (status != DETECTION_ALGORITHM_STATUS_RUNNING
+ && (primaryProviderHasReported || secondaryProviderHasReported)) {
+ throw new IllegalArgumentException(
+ "algorithmStatus=" + detectionAlgorithmStatusToString(status)
+ + ", primaryProviderReportedStatus=" + primaryProviderReportedStatus
+ + ", secondaryProviderReportedStatus="
+ + secondaryProviderReportedStatus);
+ }
+ }
+
+ /**
+ * Returns the status value of the detection algorithm.
+ */
+ public @DetectionAlgorithmStatus int getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * Returns the status of the primary location time zone provider as categorized by the detection
+ * algorithm.
+ */
+ public @ProviderStatus int getPrimaryProviderStatus() {
+ return mPrimaryProviderStatus;
+ }
+
+ /**
+ * Returns the status of the primary location time zone provider as reported by the provider
+ * itself. Can be {@code null} when the provider hasn't reported, or omitted when it has.
+ */
+ @Nullable
+ public TimeZoneProviderStatus getPrimaryProviderReportedStatus() {
+ return mPrimaryProviderReportedStatus;
+ }
+
+ /**
+ * Returns the status of the secondary location time zone provider as categorized by the
+ * detection algorithm.
+ */
+ public @ProviderStatus int getSecondaryProviderStatus() {
+ return mSecondaryProviderStatus;
+ }
+
+ /**
+ * Returns the status of the secondary location time zone provider as reported by the provider
+ * itself. Can be {@code null} when the provider hasn't reported, or omitted when it has.
+ */
+ @Nullable
+ public TimeZoneProviderStatus getSecondaryProviderReportedStatus() {
+ return mSecondaryProviderReportedStatus;
+ }
+
+ @Override
+ public String toString() {
+ return "LocationTimeZoneAlgorithmStatus{"
+ + "mAlgorithmStatus=" + detectionAlgorithmStatusToString(mStatus)
+ + ", mPrimaryProviderStatus=" + providerStatusToString(mPrimaryProviderStatus)
+ + ", mPrimaryProviderReportedStatus=" + mPrimaryProviderReportedStatus
+ + ", mSecondaryProviderStatus=" + providerStatusToString(mSecondaryProviderStatus)
+ + ", mSecondaryProviderReportedStatus=" + mSecondaryProviderReportedStatus
+ + '}';
+ }
+
+ /**
+ * Parses a {@link LocationTimeZoneAlgorithmStatus} from a toString() string for manual
+ * command-line testing.
+ */
+ @NonNull
+ public static LocationTimeZoneAlgorithmStatus parseCommandlineArg(@NonNull String arg) {
+ // Note: "}" has to be escaped on Android with "\\}" because the regexp library is not based
+ // on OpenJDK code.
+ Pattern pattern = Pattern.compile("LocationTimeZoneAlgorithmStatus\\{"
+ + "mAlgorithmStatus=(.+)"
+ + ", mPrimaryProviderStatus=([^,]+)"
+ + ", mPrimaryProviderReportedStatus=(null|TimeZoneProviderStatus\\{[^}]+\\})"
+ + ", mSecondaryProviderStatus=([^,]+)"
+ + ", mSecondaryProviderReportedStatus=(null|TimeZoneProviderStatus\\{[^}]+\\})"
+ + "\\}"
+ );
+ Matcher matcher = pattern.matcher(arg);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("Unable to parse algorithm status arg: " + arg);
+ }
+ @DetectionAlgorithmStatus int algorithmStatus =
+ detectionAlgorithmStatusFromString(matcher.group(1));
+ @ProviderStatus int primaryProviderStatus = providerStatusFromString(matcher.group(2));
+ TimeZoneProviderStatus primaryProviderReportedStatus =
+ parseTimeZoneProviderStatusOrNull(matcher.group(3));
+ @ProviderStatus int secondaryProviderStatus = providerStatusFromString(matcher.group(4));
+ TimeZoneProviderStatus secondaryProviderReportedStatus =
+ parseTimeZoneProviderStatusOrNull(matcher.group(5));
+ return new LocationTimeZoneAlgorithmStatus(
+ algorithmStatus, primaryProviderStatus, primaryProviderReportedStatus,
+ secondaryProviderStatus, secondaryProviderReportedStatus);
+ }
+
+ @Nullable
+ private static TimeZoneProviderStatus parseTimeZoneProviderStatusOrNull(
+ String providerReportedStatusString) {
+ TimeZoneProviderStatus providerReportedStatus;
+ if ("null".equals(providerReportedStatusString)) {
+ providerReportedStatus = null;
+ } else {
+ providerReportedStatus =
+ TimeZoneProviderStatus.parseProviderStatus(providerReportedStatusString);
+ }
+ return providerReportedStatus;
+ }
+
+ @NonNull
+ public static final Creator<LocationTimeZoneAlgorithmStatus> CREATOR = new Creator<>() {
+ @Override
+ public LocationTimeZoneAlgorithmStatus createFromParcel(Parcel in) {
+ @DetectionAlgorithmStatus int algorithmStatus = in.readInt();
+ @ProviderStatus int primaryProviderStatus = in.readInt();
+ TimeZoneProviderStatus primaryProviderReportedStatus =
+ in.readParcelable(getClass().getClassLoader(), TimeZoneProviderStatus.class);
+ @ProviderStatus int secondaryProviderStatus = in.readInt();
+ TimeZoneProviderStatus secondaryProviderReportedStatus =
+ in.readParcelable(getClass().getClassLoader(), TimeZoneProviderStatus.class);
+ return new LocationTimeZoneAlgorithmStatus(
+ algorithmStatus, primaryProviderStatus, primaryProviderReportedStatus,
+ secondaryProviderStatus, secondaryProviderReportedStatus);
+ }
+
+ @Override
+ public LocationTimeZoneAlgorithmStatus[] newArray(int size) {
+ return new LocationTimeZoneAlgorithmStatus[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mStatus);
+ parcel.writeInt(mPrimaryProviderStatus);
+ parcel.writeParcelable(mPrimaryProviderReportedStatus, flags);
+ parcel.writeInt(mSecondaryProviderStatus);
+ parcel.writeParcelable(mSecondaryProviderReportedStatus, flags);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ LocationTimeZoneAlgorithmStatus that = (LocationTimeZoneAlgorithmStatus) o;
+ return mStatus == that.mStatus
+ && mPrimaryProviderStatus == that.mPrimaryProviderStatus
+ && Objects.equals(
+ mPrimaryProviderReportedStatus, that.mPrimaryProviderReportedStatus)
+ && mSecondaryProviderStatus == that.mSecondaryProviderStatus
+ && Objects.equals(
+ mSecondaryProviderReportedStatus, that.mSecondaryProviderReportedStatus);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mStatus,
+ mPrimaryProviderStatus, mPrimaryProviderReportedStatus,
+ mSecondaryProviderStatus, mSecondaryProviderReportedStatus);
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ @NonNull
+ public static String providerStatusToString(@ProviderStatus int providerStatus) {
+ switch (providerStatus) {
+ case PROVIDER_STATUS_NOT_PRESENT:
+ return "NOT_PRESENT";
+ case PROVIDER_STATUS_NOT_READY:
+ return "NOT_READY";
+ case PROVIDER_STATUS_IS_CERTAIN:
+ return "IS_CERTAIN";
+ case PROVIDER_STATUS_IS_UNCERTAIN:
+ return "IS_UNCERTAIN";
+ default:
+ throw new IllegalArgumentException("Unknown status: " + providerStatus);
+ }
+ }
+
+ /** @hide */
+ @VisibleForTesting public static @ProviderStatus int providerStatusFromString(
+ @Nullable String providerStatusString) {
+ if (TextUtils.isEmpty(providerStatusString)) {
+ throw new IllegalArgumentException("Empty status: " + providerStatusString);
+ }
+
+ switch (providerStatusString) {
+ case "NOT_PRESENT":
+ return PROVIDER_STATUS_NOT_PRESENT;
+ case "NOT_READY":
+ return PROVIDER_STATUS_NOT_READY;
+ case "IS_CERTAIN":
+ return PROVIDER_STATUS_IS_CERTAIN;
+ case "IS_UNCERTAIN":
+ return PROVIDER_STATUS_IS_UNCERTAIN;
+ default:
+ throw new IllegalArgumentException("Unknown status: " + providerStatusString);
+ }
+ }
+
+ private static boolean hasProviderReported(@ProviderStatus int providerStatus) {
+ return providerStatus == PROVIDER_STATUS_IS_CERTAIN
+ || providerStatus == PROVIDER_STATUS_IS_UNCERTAIN;
+ }
+
+ /** @hide */
+ @VisibleForTesting public static @ProviderStatus int requireValidProviderStatus(
+ @ProviderStatus int providerStatus) {
+ if (providerStatus < PROVIDER_STATUS_NOT_PRESENT
+ || providerStatus > PROVIDER_STATUS_IS_UNCERTAIN) {
+ throw new IllegalArgumentException(
+ "Invalid provider status: " + providerStatus);
+ }
+ return providerStatus;
+ }
+}
diff --git a/core/java/android/app/time/TelephonyTimeZoneAlgorithmStatus.aidl b/core/java/android/app/time/TelephonyTimeZoneAlgorithmStatus.aidl
new file mode 100644
index 000000000000..0eb5b63b7ffb
--- /dev/null
+++ b/core/java/android/app/time/TelephonyTimeZoneAlgorithmStatus.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.app.time;
+
+parcelable TelephonyTimeZoneAlgorithmStatus;
diff --git a/core/java/android/app/time/TelephonyTimeZoneAlgorithmStatus.java b/core/java/android/app/time/TelephonyTimeZoneAlgorithmStatus.java
new file mode 100644
index 000000000000..95240c00fa3f
--- /dev/null
+++ b/core/java/android/app/time/TelephonyTimeZoneAlgorithmStatus.java
@@ -0,0 +1,96 @@
+/*
+ * 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.app.time;
+
+import static android.app.time.DetectorStatusTypes.detectionAlgorithmStatusToString;
+import static android.app.time.DetectorStatusTypes.requireValidDetectionAlgorithmStatus;
+
+import android.annotation.NonNull;
+import android.app.time.DetectorStatusTypes.DetectionAlgorithmStatus;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Information about the status of the telephony-based time zone detection algorithm.
+ *
+ * @hide
+ */
+public final class TelephonyTimeZoneAlgorithmStatus implements Parcelable {
+
+ private final @DetectionAlgorithmStatus int mAlgorithmStatus;
+
+ public TelephonyTimeZoneAlgorithmStatus(@DetectionAlgorithmStatus int algorithmStatus) {
+ mAlgorithmStatus = requireValidDetectionAlgorithmStatus(algorithmStatus);
+ }
+
+ /**
+ * Returns the status of the detection algorithm.
+ */
+ public @DetectionAlgorithmStatus int getAlgorithmStatus() {
+ return mAlgorithmStatus;
+ }
+
+ @Override
+ public String toString() {
+ return "TelephonyTimeZoneAlgorithmStatus{"
+ + "mAlgorithmStatus=" + detectionAlgorithmStatusToString(mAlgorithmStatus)
+ + '}';
+ }
+
+ @NonNull
+ public static final Creator<TelephonyTimeZoneAlgorithmStatus> CREATOR = new Creator<>() {
+ @Override
+ public TelephonyTimeZoneAlgorithmStatus createFromParcel(Parcel in) {
+ @DetectionAlgorithmStatus int algorithmStatus = in.readInt();
+ return new TelephonyTimeZoneAlgorithmStatus(algorithmStatus);
+ }
+
+ @Override
+ public TelephonyTimeZoneAlgorithmStatus[] newArray(int size) {
+ return new TelephonyTimeZoneAlgorithmStatus[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mAlgorithmStatus);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ TelephonyTimeZoneAlgorithmStatus that = (TelephonyTimeZoneAlgorithmStatus) o;
+ return mAlgorithmStatus == that.mAlgorithmStatus;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAlgorithmStatus);
+ }
+}
diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
index cd91b0431b28..4684c6ad811c 100644
--- a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
+++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
@@ -23,27 +23,40 @@ import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
- * A pair containing a user's {@link TimeZoneCapabilities} and {@link TimeZoneConfiguration}.
+ * An object containing a user's {@link TimeZoneCapabilities} and {@link TimeZoneConfiguration}.
*
* @hide
*/
@SystemApi
public final class TimeZoneCapabilitiesAndConfig implements Parcelable {
- public static final @NonNull Creator<TimeZoneCapabilitiesAndConfig> CREATOR =
- new Creator<TimeZoneCapabilitiesAndConfig>() {
- public TimeZoneCapabilitiesAndConfig createFromParcel(Parcel in) {
- return TimeZoneCapabilitiesAndConfig.createFromParcel(in);
- }
-
- public TimeZoneCapabilitiesAndConfig[] newArray(int size) {
- return new TimeZoneCapabilitiesAndConfig[size];
- }
- };
+ public static final @NonNull Creator<TimeZoneCapabilitiesAndConfig> CREATOR = new Creator<>() {
+ public TimeZoneCapabilitiesAndConfig createFromParcel(Parcel in) {
+ return TimeZoneCapabilitiesAndConfig.createFromParcel(in);
+ }
+ public TimeZoneCapabilitiesAndConfig[] newArray(int size) {
+ return new TimeZoneCapabilitiesAndConfig[size];
+ }
+ };
+ /**
+ * The time zone detector status.
+ *
+ * Implementation note for future platform engineers: This field is only needed by SettingsUI
+ * initially and so it has not been added to the SDK API. {@link TimeZoneDetectorStatus}
+ * contains details about the internals of the time zone detector so thought should be given to
+ * abstraction / exposing a lightweight version if something unbundled needs access to detector
+ * details. Also, that could be good time to add separate APIs for bundled components, or add
+ * new APIs that return something more extensible and generic like a Bundle or a less
+ * constraining name. See also {@link
+ * TimeManager#addTimeZoneDetectorListener(Executor, TimeManager.TimeZoneDetectorListener)},
+ * which notified of changes to any fields in this class, including the detector status.
+ */
+ @NonNull private final TimeZoneDetectorStatus mDetectorStatus;
@NonNull private final TimeZoneCapabilities mCapabilities;
@NonNull private final TimeZoneConfiguration mConfiguration;
@@ -53,26 +66,41 @@ public final class TimeZoneCapabilitiesAndConfig implements Parcelable {
* @hide
*/
public TimeZoneCapabilitiesAndConfig(
+ @NonNull TimeZoneDetectorStatus detectorStatus,
@NonNull TimeZoneCapabilities capabilities,
@NonNull TimeZoneConfiguration configuration) {
- this.mCapabilities = Objects.requireNonNull(capabilities);
- this.mConfiguration = Objects.requireNonNull(configuration);
+ mDetectorStatus = Objects.requireNonNull(detectorStatus);
+ mCapabilities = Objects.requireNonNull(capabilities);
+ mConfiguration = Objects.requireNonNull(configuration);
}
@NonNull
private static TimeZoneCapabilitiesAndConfig createFromParcel(Parcel in) {
- TimeZoneCapabilities capabilities = in.readParcelable(null, android.app.time.TimeZoneCapabilities.class);
- TimeZoneConfiguration configuration = in.readParcelable(null, android.app.time.TimeZoneConfiguration.class);
- return new TimeZoneCapabilitiesAndConfig(capabilities, configuration);
+ TimeZoneDetectorStatus detectorStatus =
+ in.readParcelable(null, TimeZoneDetectorStatus.class);
+ TimeZoneCapabilities capabilities = in.readParcelable(null, TimeZoneCapabilities.class);
+ TimeZoneConfiguration configuration = in.readParcelable(null, TimeZoneConfiguration.class);
+ return new TimeZoneCapabilitiesAndConfig(detectorStatus, capabilities, configuration);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mDetectorStatus, flags);
dest.writeParcelable(mCapabilities, flags);
dest.writeParcelable(mConfiguration, flags);
}
/**
+ * Returns the time zone detector's status.
+ *
+ * @hide
+ */
+ @NonNull
+ public TimeZoneDetectorStatus getDetectorStatus() {
+ return mDetectorStatus;
+ }
+
+ /**
* Returns the user's time zone behavior capabilities.
*/
@NonNull
@@ -102,7 +130,8 @@ public final class TimeZoneCapabilitiesAndConfig implements Parcelable {
return false;
}
TimeZoneCapabilitiesAndConfig that = (TimeZoneCapabilitiesAndConfig) o;
- return mCapabilities.equals(that.mCapabilities)
+ return mDetectorStatus.equals(that.mDetectorStatus)
+ && mCapabilities.equals(that.mCapabilities)
&& mConfiguration.equals(that.mConfiguration);
}
@@ -114,7 +143,8 @@ public final class TimeZoneCapabilitiesAndConfig implements Parcelable {
@Override
public String toString() {
return "TimeZoneCapabilitiesAndConfig{"
- + "mCapabilities=" + mCapabilities
+ + "mDetectorStatus=" + mDetectorStatus
+ + ", mCapabilities=" + mCapabilities
+ ", mConfiguration=" + mConfiguration
+ '}';
}
diff --git a/core/java/android/app/time/TimeZoneDetectorStatus.aidl b/core/java/android/app/time/TimeZoneDetectorStatus.aidl
new file mode 100644
index 000000000000..32204df6d698
--- /dev/null
+++ b/core/java/android/app/time/TimeZoneDetectorStatus.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.app.time;
+
+parcelable TimeZoneDetectorStatus;
diff --git a/core/java/android/app/time/TimeZoneDetectorStatus.java b/core/java/android/app/time/TimeZoneDetectorStatus.java
new file mode 100644
index 000000000000..16374639b77c
--- /dev/null
+++ b/core/java/android/app/time/TimeZoneDetectorStatus.java
@@ -0,0 +1,124 @@
+/*
+ * 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.app.time;
+
+import static android.app.time.DetectorStatusTypes.DetectorStatus;
+import static android.app.time.DetectorStatusTypes.requireValidDetectorStatus;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Information about the status of the automatic time zone detector. Used by SettingsUI to display
+ * status information to the user.
+ *
+ * @hide
+ */
+public final class TimeZoneDetectorStatus implements Parcelable {
+
+ private final @DetectorStatus int mDetectorStatus;
+ @NonNull private final TelephonyTimeZoneAlgorithmStatus mTelephonyTimeZoneAlgorithmStatus;
+ @NonNull private final LocationTimeZoneAlgorithmStatus mLocationTimeZoneAlgorithmStatus;
+
+ public TimeZoneDetectorStatus(
+ @DetectorStatus int detectorStatus,
+ @NonNull TelephonyTimeZoneAlgorithmStatus telephonyTimeZoneAlgorithmStatus,
+ @NonNull LocationTimeZoneAlgorithmStatus locationTimeZoneAlgorithmStatus) {
+ mDetectorStatus = requireValidDetectorStatus(detectorStatus);
+ mTelephonyTimeZoneAlgorithmStatus =
+ Objects.requireNonNull(telephonyTimeZoneAlgorithmStatus);
+ mLocationTimeZoneAlgorithmStatus = Objects.requireNonNull(locationTimeZoneAlgorithmStatus);
+ }
+
+ public @DetectorStatus int getDetectorStatus() {
+ return mDetectorStatus;
+ }
+
+ @NonNull
+ public TelephonyTimeZoneAlgorithmStatus getTelephonyTimeZoneAlgorithmStatus() {
+ return mTelephonyTimeZoneAlgorithmStatus;
+ }
+
+ @NonNull
+ public LocationTimeZoneAlgorithmStatus getLocationTimeZoneAlgorithmStatus() {
+ return mLocationTimeZoneAlgorithmStatus;
+ }
+
+ @Override
+ public String toString() {
+ return "TimeZoneDetectorStatus{"
+ + "mDetectorStatus=" + DetectorStatusTypes.detectorStatusToString(mDetectorStatus)
+ + ", mTelephonyTimeZoneAlgorithmStatus=" + mTelephonyTimeZoneAlgorithmStatus
+ + ", mLocationTimeZoneAlgorithmStatus=" + mLocationTimeZoneAlgorithmStatus
+ + '}';
+ }
+
+ public static final @NonNull Creator<TimeZoneDetectorStatus> CREATOR = new Creator<>() {
+ @Override
+ public TimeZoneDetectorStatus createFromParcel(Parcel in) {
+ @DetectorStatus int detectorStatus = in.readInt();
+ TelephonyTimeZoneAlgorithmStatus telephonyTimeZoneAlgorithmStatus =
+ in.readParcelable(getClass().getClassLoader(),
+ TelephonyTimeZoneAlgorithmStatus.class);
+ LocationTimeZoneAlgorithmStatus locationTimeZoneAlgorithmStatus =
+ in.readParcelable(getClass().getClassLoader(),
+ LocationTimeZoneAlgorithmStatus.class);
+ return new TimeZoneDetectorStatus(detectorStatus,
+ telephonyTimeZoneAlgorithmStatus, locationTimeZoneAlgorithmStatus);
+ }
+
+ @Override
+ public TimeZoneDetectorStatus[] newArray(int size) {
+ return new TimeZoneDetectorStatus[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mDetectorStatus);
+ parcel.writeParcelable(mTelephonyTimeZoneAlgorithmStatus, flags);
+ parcel.writeParcelable(mLocationTimeZoneAlgorithmStatus, flags);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ TimeZoneDetectorStatus that = (TimeZoneDetectorStatus) o;
+ return mDetectorStatus == that.mDetectorStatus
+ && mTelephonyTimeZoneAlgorithmStatus.equals(that.mTelephonyTimeZoneAlgorithmStatus)
+ && mLocationTimeZoneAlgorithmStatus.equals(that.mLocationTimeZoneAlgorithmStatus);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDetectorStatus, mTelephonyTimeZoneAlgorithmStatus,
+ mLocationTimeZoneAlgorithmStatus);
+ }
+}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index 0e9e28be8818..f357fb243fe1 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -81,11 +81,11 @@ public interface TimeZoneDetector {
String SHELL_COMMAND_SET_GEO_DETECTION_ENABLED = "set_geo_detection_enabled";
/**
- * A shell command that injects a geolocation time zone suggestion (as if from the
+ * A shell command that injects a location algorithm event (as if from the
* location_time_zone_manager).
* @hide
*/
- String SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE = "suggest_geo_location_time_zone";
+ String SHELL_COMMAND_HANDLE_LOCATION_ALGORITHM_EVENT = "handle_location_algorithm_event";
/**
* A shell command that injects a manual time zone suggestion (as if from the SettingsUI or
diff --git a/core/proto/android/app/location_time_zone_manager.proto b/core/proto/android/app/location_time_zone_manager.proto
index 5fdcfdf35a37..7037a6c4f68a 100644
--- a/core/proto/android/app/location_time_zone_manager.proto
+++ b/core/proto/android/app/location_time_zone_manager.proto
@@ -40,7 +40,7 @@ enum ControllerStateEnum {
message LocationTimeZoneManagerServiceStateProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
- optional GeolocationTimeZoneSuggestionProto last_suggestion = 1;
+ optional LocationTimeZoneProviderEventProto last_event = 1;
repeated TimeZoneProviderStateProto primary_provider_states = 2;
repeated TimeZoneProviderStateProto secondary_provider_states = 3;
repeated ControllerStateEnum controller_states = 4;
diff --git a/core/proto/android/app/time_zone_detector.proto b/core/proto/android/app/time_zone_detector.proto
index b52aa828bef9..cd4a36fafef0 100644
--- a/core/proto/android/app/time_zone_detector.proto
+++ b/core/proto/android/app/time_zone_detector.proto
@@ -22,13 +22,38 @@ import "frameworks/base/core/proto/android/privacy.proto";
option java_multiple_files = true;
option java_outer_classname = "TimeZoneDetectorProto";
-// Represents a GeolocationTimeZoneSuggestion that can be / has been passed to the time zone
+// Represents a LocationTimeZoneProviderEvent that can be / has been passed to the time zone
// detector.
+message LocationTimeZoneProviderEventProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional GeolocationTimeZoneSuggestionProto suggestion = 1;
+ repeated string debug_info = 2;
+ optional LocationTimeZoneAlgorithmStatusProto algorithm_status = 3;
+}
+
+// Represents a LocationTimeZoneAlgorithmStatus that can be / has been passed to the time zone
+// detector.
+message LocationTimeZoneAlgorithmStatusProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional DetectionAlgorithmStatusEnum status = 1;
+}
+
+// The state enum for detection algorithms.
+enum DetectionAlgorithmStatusEnum {
+ DETECTION_ALGORITHM_STATUS_UNKNOWN = 0;
+ DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED = 1;
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING = 2;
+ DETECTION_ALGORITHM_STATUS_RUNNING = 3;
+}
+
+// Represents a GeolocationTimeZoneSuggestion that can be contained in a
+// LocationTimeZoneProviderEvent.
message GeolocationTimeZoneSuggestionProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
repeated string zone_ids = 1;
- repeated string debug_info = 2;
}
/*
diff --git a/core/tests/coretests/src/android/app/time/DetectorStatusTypesTest.java b/core/tests/coretests/src/android/app/time/DetectorStatusTypesTest.java
new file mode 100644
index 000000000000..f57ee43b76c8
--- /dev/null
+++ b/core/tests/coretests/src/android/app/time/DetectorStatusTypesTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 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.app.time;
+
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_UNKNOWN;
+import static android.app.time.DetectorStatusTypes.DETECTOR_STATUS_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTOR_STATUS_UNKNOWN;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import android.app.time.DetectorStatusTypes.DetectionAlgorithmStatus;
+import android.app.time.DetectorStatusTypes.DetectorStatus;
+
+import org.junit.Test;
+
+public class DetectorStatusTypesTest {
+
+ @Test
+ public void testRequireValidDetectionAlgorithmStatus() {
+ for (@DetectionAlgorithmStatus int status = DETECTION_ALGORITHM_STATUS_UNKNOWN;
+ status <= DETECTION_ALGORITHM_STATUS_RUNNING; status++) {
+ assertEquals(status, DetectorStatusTypes.requireValidDetectionAlgorithmStatus(status));
+ }
+
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.requireValidDetectionAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_UNKNOWN - 1));
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.requireValidDetectionAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING + 1));
+ }
+
+ @Test
+ public void testFormatAndParseDetectionAlgorithmStatus() {
+ for (@DetectionAlgorithmStatus int status = DETECTION_ALGORITHM_STATUS_UNKNOWN;
+ status <= DETECTION_ALGORITHM_STATUS_RUNNING; status++) {
+ assertEquals(status, DetectorStatusTypes.detectionAlgorithmStatusFromString(
+ DetectorStatusTypes.detectionAlgorithmStatusToString(status)));
+ }
+
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.detectorStatusToString(
+ DETECTION_ALGORITHM_STATUS_UNKNOWN - 1));
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.detectorStatusToString(
+ DETECTION_ALGORITHM_STATUS_RUNNING + 1));
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.detectorStatusFromString(null));
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.detectorStatusFromString(""));
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.detectorStatusFromString("FOO"));
+ }
+
+ @Test
+ public void testRequireValidDetectorStatus() {
+ for (@DetectorStatus int status = DETECTOR_STATUS_UNKNOWN;
+ status <= DETECTOR_STATUS_RUNNING; status++) {
+ assertEquals(status, DetectorStatusTypes.requireValidDetectorStatus(status));
+ }
+
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.requireValidDetectorStatus(DETECTOR_STATUS_UNKNOWN - 1));
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.requireValidDetectorStatus(DETECTOR_STATUS_RUNNING + 1));
+ }
+
+ @Test
+ public void testFormatAndParseDetectorStatus() {
+ for (@DetectorStatus int status = DETECTOR_STATUS_UNKNOWN;
+ status <= DETECTOR_STATUS_RUNNING; status++) {
+ assertEquals(status, DetectorStatusTypes.detectorStatusFromString(
+ DetectorStatusTypes.detectorStatusToString(status)));
+ }
+
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.detectorStatusToString(DETECTOR_STATUS_UNKNOWN - 1));
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.detectorStatusToString(DETECTOR_STATUS_RUNNING + 1));
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.detectorStatusFromString(null));
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.detectorStatusFromString(""));
+ assertThrows(IllegalArgumentException.class,
+ () -> DetectorStatusTypes.detectorStatusFromString("FOO"));
+ }
+}
diff --git a/core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java b/core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java
new file mode 100644
index 000000000000..a648a885aea2
--- /dev/null
+++ b/core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 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.app.time;
+
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_CERTAIN;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_UNCERTAIN;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_PRESENT;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_READY;
+import static android.app.time.ParcelableTestSupport.assertEqualsAndHashCode;
+import static android.app.time.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK;
+import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_OK;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThrows;
+
+import android.app.time.LocationTimeZoneAlgorithmStatus.ProviderStatus;
+import android.service.timezone.TimeZoneProviderStatus;
+
+import org.junit.Test;
+
+public class LocationTimeZoneAlgorithmStatusTest {
+
+ private static final TimeZoneProviderStatus ARBITRARY_PROVIDER_RUNNING_STATUS =
+ new TimeZoneProviderStatus.Builder()
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
+ .build();
+
+ @Test
+ public void testConstructorValidation() {
+ // Sample some invalid cases
+
+ // There can't be a reported provider status if the algorithm isn't running.
+ new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_IS_CERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS,
+ PROVIDER_STATUS_IS_UNCERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS);
+ assertThrows(IllegalArgumentException.class,
+ () -> new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_NOT_RUNNING,
+ PROVIDER_STATUS_IS_CERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS,
+ PROVIDER_STATUS_IS_UNCERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS));
+
+ new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_IS_CERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS,
+ PROVIDER_STATUS_NOT_PRESENT, null);
+ assertThrows(IllegalArgumentException.class,
+ () -> new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_NOT_RUNNING,
+ PROVIDER_STATUS_IS_CERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS,
+ PROVIDER_STATUS_NOT_PRESENT, null));
+
+ new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_NOT_PRESENT, null,
+ PROVIDER_STATUS_IS_UNCERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS);
+ assertThrows(IllegalArgumentException.class,
+ () -> new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_NOT_RUNNING,
+ PROVIDER_STATUS_NOT_PRESENT, null,
+ PROVIDER_STATUS_IS_UNCERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS));
+
+ // No reported provider status expected if the associated provider isn't ready / present.
+ new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_NOT_PRESENT, null,
+ PROVIDER_STATUS_NOT_PRESENT, null);
+ assertThrows(IllegalArgumentException.class,
+ () -> new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_NOT_PRESENT, ARBITRARY_PROVIDER_RUNNING_STATUS,
+ PROVIDER_STATUS_NOT_PRESENT, null));
+ new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_NOT_READY, null,
+ PROVIDER_STATUS_NOT_PRESENT, null);
+ assertThrows(IllegalArgumentException.class,
+ () -> new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_NOT_READY, null,
+ PROVIDER_STATUS_NOT_PRESENT, ARBITRARY_PROVIDER_RUNNING_STATUS));
+ }
+
+ @Test
+ public void testEquals() {
+ LocationTimeZoneAlgorithmStatus one = new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_IS_CERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS,
+ PROVIDER_STATUS_NOT_PRESENT, null);
+ assertEqualsAndHashCode(one, one);
+
+ {
+ LocationTimeZoneAlgorithmStatus two = new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_IS_CERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS,
+ PROVIDER_STATUS_NOT_PRESENT, null);
+ assertEqualsAndHashCode(one, two);
+ }
+
+ {
+ LocationTimeZoneAlgorithmStatus three = new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING,
+ PROVIDER_STATUS_NOT_READY, null,
+ PROVIDER_STATUS_NOT_PRESENT, null);
+ assertNotEquals(one, three);
+ assertNotEquals(three, one);
+ }
+ }
+
+ @Test
+ public void testParcelable() {
+ // Primary provider only.
+ {
+ LocationTimeZoneAlgorithmStatus locationAlgorithmStatus =
+ new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_IS_CERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS,
+ PROVIDER_STATUS_NOT_PRESENT, null);
+ assertRoundTripParcelable(locationAlgorithmStatus);
+ }
+
+ // Secondary provider only
+ {
+ LocationTimeZoneAlgorithmStatus locationAlgorithmStatus =
+ new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_NOT_PRESENT, null,
+ PROVIDER_STATUS_IS_CERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS);
+ assertRoundTripParcelable(locationAlgorithmStatus);
+ }
+
+ // Algorithm not running.
+ {
+ LocationTimeZoneAlgorithmStatus locationAlgorithmStatus =
+ new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING,
+ PROVIDER_STATUS_NOT_PRESENT, null,
+ PROVIDER_STATUS_NOT_PRESENT, null);
+ assertRoundTripParcelable(locationAlgorithmStatus);
+ }
+ }
+
+ @Test
+ public void testRequireValidProviderStatus() {
+ for (@ProviderStatus int status = PROVIDER_STATUS_NOT_PRESENT;
+ status <= PROVIDER_STATUS_IS_UNCERTAIN; status++) {
+ assertEquals(status,
+ LocationTimeZoneAlgorithmStatus.requireValidProviderStatus(status));
+ }
+
+ assertThrows(IllegalArgumentException.class,
+ () -> LocationTimeZoneAlgorithmStatus.requireValidProviderStatus(
+ PROVIDER_STATUS_NOT_PRESENT - 1));
+ assertThrows(IllegalArgumentException.class,
+ () -> LocationTimeZoneAlgorithmStatus.requireValidProviderStatus(
+ PROVIDER_STATUS_IS_UNCERTAIN + 1));
+ }
+
+ @Test
+ public void testFormatAndParseProviderStatus() {
+ for (@ProviderStatus int status = PROVIDER_STATUS_NOT_PRESENT;
+ status <= PROVIDER_STATUS_IS_UNCERTAIN; status++) {
+ assertEquals(status, LocationTimeZoneAlgorithmStatus.providerStatusFromString(
+ LocationTimeZoneAlgorithmStatus.providerStatusToString(status)));
+ }
+
+ assertThrows(IllegalArgumentException.class,
+ () -> LocationTimeZoneAlgorithmStatus.providerStatusToString(
+ PROVIDER_STATUS_NOT_PRESENT - 1));
+ assertThrows(IllegalArgumentException.class,
+ () -> LocationTimeZoneAlgorithmStatus.providerStatusToString(
+ PROVIDER_STATUS_IS_UNCERTAIN + 1));
+ assertThrows(IllegalArgumentException.class,
+ () -> LocationTimeZoneAlgorithmStatus.providerStatusFromString(null));
+ assertThrows(IllegalArgumentException.class,
+ () -> LocationTimeZoneAlgorithmStatus.providerStatusFromString(""));
+ assertThrows(IllegalArgumentException.class,
+ () -> LocationTimeZoneAlgorithmStatus.providerStatusFromString("FOO"));
+ }
+
+ @Test
+ public void testParseCommandlineArg_noNullReportedStatuses() {
+ LocationTimeZoneAlgorithmStatus status = new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_IS_CERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS,
+ PROVIDER_STATUS_IS_UNCERTAIN, ARBITRARY_PROVIDER_RUNNING_STATUS);
+ assertEquals(status,
+ LocationTimeZoneAlgorithmStatus.parseCommandlineArg(status.toString()));
+ }
+
+ @Test
+ public void testParseCommandlineArg_withNullReportedStatuses() {
+ LocationTimeZoneAlgorithmStatus status = new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_IS_CERTAIN, null,
+ PROVIDER_STATUS_IS_UNCERTAIN, null);
+ assertEquals(status,
+ LocationTimeZoneAlgorithmStatus.parseCommandlineArg(status.toString()));
+ }
+}
diff --git a/core/tests/coretests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java b/core/tests/coretests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java
new file mode 100644
index 000000000000..b90c485bbbb6
--- /dev/null
+++ b/core/tests/coretests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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.app.time;
+
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
+import static android.app.time.ParcelableTestSupport.assertEqualsAndHashCode;
+import static android.app.time.ParcelableTestSupport.assertRoundTripParcelable;
+
+import static org.junit.Assert.assertNotEquals;
+
+import org.junit.Test;
+
+public class TelephonyTimeZoneAlgorithmStatusTest {
+
+ @Test
+ public void testEquals() {
+ TelephonyTimeZoneAlgorithmStatus one = new TelephonyTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
+ assertEqualsAndHashCode(one, one);
+
+ {
+ TelephonyTimeZoneAlgorithmStatus two = new TelephonyTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
+ assertEqualsAndHashCode(one, two);
+ }
+
+ {
+ TelephonyTimeZoneAlgorithmStatus three = new TelephonyTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING);
+ assertNotEquals(one, three);
+ assertNotEquals(three, one);
+ }
+ }
+
+ @Test
+ public void testParcelable() {
+ // Algorithm running.
+ {
+ TelephonyTimeZoneAlgorithmStatus locationAlgorithmStatus =
+ new TelephonyTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
+ assertRoundTripParcelable(locationAlgorithmStatus);
+ }
+
+ // Algorithm not running.
+ {
+ TelephonyTimeZoneAlgorithmStatus locationAlgorithmStatus =
+ new TelephonyTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING);
+ assertRoundTripParcelable(locationAlgorithmStatus);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneDetectorStatusTest.java b/core/tests/coretests/src/android/app/time/TimeZoneDetectorStatusTest.java
new file mode 100644
index 000000000000..dfff7ecdf989
--- /dev/null
+++ b/core/tests/coretests/src/android/app/time/TimeZoneDetectorStatusTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 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.app.time;
+
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTOR_STATUS_NOT_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTOR_STATUS_RUNNING;
+import static android.app.time.ParcelableTestSupport.assertEqualsAndHashCode;
+import static android.app.time.ParcelableTestSupport.assertRoundTripParcelable;
+
+import static org.junit.Assert.assertNotEquals;
+
+import org.junit.Test;
+
+public class TimeZoneDetectorStatusTest {
+
+ private static final TelephonyTimeZoneAlgorithmStatus ARBITRARY_TELEPHONY_ALGORITHM_STATUS =
+ new TelephonyTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING);
+
+ private static final LocationTimeZoneAlgorithmStatus ARBITRARY_LOCATION_ALGORITHM_STATUS =
+ new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING,
+ LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_READY, null,
+ LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_PRESENT, null);
+
+ @Test
+ public void testEquals() {
+ TimeZoneDetectorStatus one = new TimeZoneDetectorStatus(DETECTOR_STATUS_RUNNING,
+ ARBITRARY_TELEPHONY_ALGORITHM_STATUS, ARBITRARY_LOCATION_ALGORITHM_STATUS);
+ assertEqualsAndHashCode(one, one);
+
+ {
+ TimeZoneDetectorStatus two = new TimeZoneDetectorStatus(DETECTOR_STATUS_RUNNING,
+ ARBITRARY_TELEPHONY_ALGORITHM_STATUS, ARBITRARY_LOCATION_ALGORITHM_STATUS);
+ assertEqualsAndHashCode(one, two);
+ }
+
+ {
+ TimeZoneDetectorStatus three = new TimeZoneDetectorStatus(DETECTOR_STATUS_NOT_RUNNING,
+ ARBITRARY_TELEPHONY_ALGORITHM_STATUS, ARBITRARY_LOCATION_ALGORITHM_STATUS);
+ assertNotEquals(one, three);
+ assertNotEquals(three, one);
+ }
+
+ {
+ TelephonyTimeZoneAlgorithmStatus telephonyAlgorithmStatus =
+ new TelephonyTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_NOT_RUNNING);
+ assertNotEquals(telephonyAlgorithmStatus, ARBITRARY_TELEPHONY_ALGORITHM_STATUS);
+
+ TimeZoneDetectorStatus three = new TimeZoneDetectorStatus(DETECTOR_STATUS_NOT_RUNNING,
+ telephonyAlgorithmStatus, ARBITRARY_LOCATION_ALGORITHM_STATUS);
+ assertNotEquals(one, three);
+ assertNotEquals(three, one);
+ }
+
+ {
+ LocationTimeZoneAlgorithmStatus locationAlgorithmStatus =
+ new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING,
+ LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_READY, null,
+ LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_READY, null);
+ assertNotEquals(locationAlgorithmStatus, ARBITRARY_LOCATION_ALGORITHM_STATUS);
+
+ TimeZoneDetectorStatus three = new TimeZoneDetectorStatus(DETECTOR_STATUS_NOT_RUNNING,
+ ARBITRARY_TELEPHONY_ALGORITHM_STATUS, locationAlgorithmStatus);
+ assertNotEquals(one, three);
+ assertNotEquals(three, one);
+ }
+ }
+
+ @Test
+ public void testParcelable() {
+ // Detector running.
+ {
+ TimeZoneDetectorStatus locationAlgorithmStatus = new TimeZoneDetectorStatus(
+ DETECTOR_STATUS_RUNNING, ARBITRARY_TELEPHONY_ALGORITHM_STATUS,
+ ARBITRARY_LOCATION_ALGORITHM_STATUS);
+ assertRoundTripParcelable(locationAlgorithmStatus);
+ }
+
+ // Detector not running.
+ {
+ TimeZoneDetectorStatus locationAlgorithmStatus =
+ new TimeZoneDetectorStatus(DETECTOR_STATUS_NOT_RUNNING,
+ ARBITRARY_TELEPHONY_ALGORITHM_STATUS,
+ ARBITRARY_LOCATION_ALGORITHM_STATUS);
+ assertRoundTripParcelable(locationAlgorithmStatus);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
index 8218fa5c4643..80d959977b0f 100644
--- a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
+++ b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
@@ -19,20 +19,15 @@ package com.android.server.timezonedetector;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.ShellCommand;
-import android.os.SystemClock;
-import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.StringTokenizer;
/**
- * A time zone suggestion from the location_time_zone_manager service to the time_zone_detector
- * service.
+ * A time zone suggestion from the location_time_zone_manager service (AKA the location-based time
+ * zone detection algorithm).
*
* <p>Geolocation-based suggestions have the following properties:
*
@@ -63,24 +58,16 @@ import java.util.StringTokenizer;
* location_time_zone_manager may become uncertain if components further downstream cannot
* determine the device's location with sufficient accuracy, or if the location is known but no
* time zone can be determined because no time zone mapping information is available.</li>
- * <li>{@code debugInfo} contains debugging metadata associated with the suggestion. This is
- * used to record why the suggestion exists and how it was obtained. This information exists
- * only to aid in debugging and therefore is used by {@link #toString()}, but it is not for use
- * in detection logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
* </li>
* </ul>
- *
- * @hide
*/
public final class GeolocationTimeZoneSuggestion {
@ElapsedRealtimeLong private final long mEffectiveFromElapsedMillis;
@Nullable private final List<String> mZoneIds;
- @Nullable private ArrayList<String> mDebugInfo;
private GeolocationTimeZoneSuggestion(
- @ElapsedRealtimeLong long effectiveFromElapsedMillis,
- @Nullable List<String> zoneIds) {
+ @ElapsedRealtimeLong long effectiveFromElapsedMillis, @Nullable List<String> zoneIds) {
mEffectiveFromElapsedMillis = effectiveFromElapsedMillis;
if (zoneIds == null) {
// Unopinionated
@@ -104,8 +91,7 @@ public final class GeolocationTimeZoneSuggestion {
*/
@NonNull
public static GeolocationTimeZoneSuggestion createCertainSuggestion(
- @ElapsedRealtimeLong long effectiveFromElapsedMillis,
- @NonNull List<String> zoneIds) {
+ @ElapsedRealtimeLong long effectiveFromElapsedMillis, @NonNull List<String> zoneIds) {
return new GeolocationTimeZoneSuggestion(effectiveFromElapsedMillis, zoneIds);
}
@@ -126,25 +112,6 @@ public final class GeolocationTimeZoneSuggestion {
return mZoneIds;
}
- /** Returns debug information. See {@link GeolocationTimeZoneSuggestion} for details. */
- @NonNull
- public List<String> getDebugInfo() {
- return mDebugInfo == null
- ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
- }
-
- /**
- * Associates information with the instance that can be useful for debugging / logging. The
- * information is present in {@link #toString()} but is not considered for
- * {@link #equals(Object)} and {@link #hashCode()}.
- */
- public void addDebugInfo(String... debugInfos) {
- if (mDebugInfo == null) {
- mDebugInfo = new ArrayList<>();
- }
- mDebugInfo.addAll(Arrays.asList(debugInfos));
- }
-
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -169,59 +136,6 @@ public final class GeolocationTimeZoneSuggestion {
return "GeolocationTimeZoneSuggestion{"
+ "mEffectiveFromElapsedMillis=" + mEffectiveFromElapsedMillis
+ ", mZoneIds=" + mZoneIds
- + ", mDebugInfo=" + mDebugInfo
+ '}';
}
-
- /** @hide */
- public static GeolocationTimeZoneSuggestion parseCommandLineArg(@NonNull ShellCommand cmd) {
- String zoneIdsString = null;
- String opt;
- while ((opt = cmd.getNextArg()) != null) {
- switch (opt) {
- case "--zone_ids": {
- zoneIdsString = cmd.getNextArgRequired();
- break;
- }
- default: {
- throw new IllegalArgumentException("Unknown option: " + opt);
- }
- }
- }
-
- if (zoneIdsString == null) {
- throw new IllegalArgumentException("Missing --zone_ids");
- }
-
- long elapsedRealtimeMillis = SystemClock.elapsedRealtime();
- List<String> zoneIds = parseZoneIdsArg(zoneIdsString);
- GeolocationTimeZoneSuggestion suggestion =
- new GeolocationTimeZoneSuggestion(elapsedRealtimeMillis, zoneIds);
- suggestion.addDebugInfo("Command line injection");
- return suggestion;
- }
-
- private static List<String> parseZoneIdsArg(String zoneIdsString) {
- if ("UNCERTAIN".equals(zoneIdsString)) {
- return null;
- } else if ("EMPTY".equals(zoneIdsString)) {
- return Collections.emptyList();
- } else {
- ArrayList<String> zoneIds = new ArrayList<>();
- StringTokenizer tokenizer = new StringTokenizer(zoneIdsString, ",");
- while (tokenizer.hasMoreTokens()) {
- zoneIds.add(tokenizer.nextToken());
- }
- return zoneIds;
- }
- }
-
- /** @hide */
- public static void printCommandLineOpts(@NonNull PrintWriter pw) {
- pw.println("Geolocation suggestion options:");
- pw.println(" --zone_ids {UNCERTAIN|EMPTY|<Olson ID>+}");
- pw.println();
- pw.println("See " + GeolocationTimeZoneSuggestion.class.getName()
- + " for more information");
- }
}
diff --git a/services/core/java/com/android/server/timezonedetector/LocationAlgorithmEvent.java b/services/core/java/com/android/server/timezonedetector/LocationAlgorithmEvent.java
new file mode 100644
index 000000000000..1ffd9a11b300
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/LocationAlgorithmEvent.java
@@ -0,0 +1,194 @@
+/*
+ * 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 com.android.server.timezonedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.time.LocationTimeZoneAlgorithmStatus;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.StringTokenizer;
+
+/**
+ * An event from the location_time_zone_manager service (AKA the location-based time zone detection
+ * algorithm). An event can represent a new time zone recommendation, an algorithm status change, or
+ * both.
+ *
+ * <p>Events have the following properties:
+ *
+ * <ul>
+ * <li>{@code algorithmStatus}: The current status of the location-based time zone detection
+ * algorithm.</li>
+ * <li>{@code suggestion}: The latest time zone suggestion, if there is one.</li>
+ * <li>{@code debugInfo} contains debugging metadata associated with the suggestion. This is
+ * used to record why the event exists and how information contained within it was obtained.
+ * This information exists only to aid in debugging and therefore is used by
+ * {@link #toString()}, but it is not for use in detection logic and is not considered in
+ * {@link #hashCode()} or {@link #equals(Object)}.
+ * </li>
+ * </ul>
+ */
+public final class LocationAlgorithmEvent {
+
+ @NonNull private final LocationTimeZoneAlgorithmStatus mAlgorithmStatus;
+ @Nullable private final GeolocationTimeZoneSuggestion mSuggestion;
+ @Nullable private ArrayList<String> mDebugInfo;
+
+ /** Creates a new instance. */
+ public LocationAlgorithmEvent(
+ @NonNull LocationTimeZoneAlgorithmStatus algorithmStatus,
+ @Nullable GeolocationTimeZoneSuggestion suggestion) {
+ mAlgorithmStatus = Objects.requireNonNull(algorithmStatus);
+ mSuggestion = suggestion;
+ }
+
+ /**
+ * Returns the status of the location time zone detector algorithm.
+ */
+ @NonNull
+ public LocationTimeZoneAlgorithmStatus getAlgorithmStatus() {
+ return mAlgorithmStatus;
+ }
+
+ /**
+ * Returns the latest location algorithm suggestion. See {@link LocationAlgorithmEvent} for
+ * details.
+ */
+ @Nullable
+ public GeolocationTimeZoneSuggestion getSuggestion() {
+ return mSuggestion;
+ }
+
+ /** Returns debug information. See {@link LocationAlgorithmEvent} for details. */
+ @NonNull
+ public List<String> getDebugInfo() {
+ return mDebugInfo == null
+ ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
+ }
+
+ /**
+ * Associates information with the instance that can be useful for debugging / logging. The
+ * information is present in {@link #toString()} but is not considered for
+ * {@link #equals(Object)} and {@link #hashCode()}.
+ */
+ public void addDebugInfo(String... debugInfos) {
+ if (mDebugInfo == null) {
+ mDebugInfo = new ArrayList<>();
+ }
+ mDebugInfo.addAll(Arrays.asList(debugInfos));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ LocationAlgorithmEvent that = (LocationAlgorithmEvent) o;
+ return mAlgorithmStatus.equals(that.mAlgorithmStatus)
+ && Objects.equals(mSuggestion, that.mSuggestion);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAlgorithmStatus, mSuggestion);
+ }
+
+ @Override
+ public String toString() {
+ return "LocationAlgorithmEvent{"
+ + "mAlgorithmStatus=" + mAlgorithmStatus
+ + ", mSuggestion=" + mSuggestion
+ + ", mDebugInfo=" + mDebugInfo
+ + '}';
+ }
+
+ static LocationAlgorithmEvent parseCommandLineArg(@NonNull ShellCommand cmd) {
+ String suggestionString = null;
+ LocationTimeZoneAlgorithmStatus algorithmStatus = null;
+ String opt;
+ while ((opt = cmd.getNextArg()) != null) {
+ switch (opt) {
+ case "--status": {
+ algorithmStatus = LocationTimeZoneAlgorithmStatus.parseCommandlineArg(
+ cmd.getNextArgRequired());
+ break;
+ }
+ case "--suggestion": {
+ suggestionString = cmd.getNextArgRequired();
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
+ }
+ }
+
+ if (algorithmStatus == null) {
+ throw new IllegalArgumentException("Missing --status");
+ }
+
+ GeolocationTimeZoneSuggestion suggestion = null;
+ if (suggestionString != null) {
+ List<String> zoneIds = parseZoneIds(suggestionString);
+ long elapsedRealtimeMillis = SystemClock.elapsedRealtime();
+ if (zoneIds == null) {
+ suggestion = GeolocationTimeZoneSuggestion.createUncertainSuggestion(
+ elapsedRealtimeMillis);
+ } else {
+ suggestion = GeolocationTimeZoneSuggestion.createCertainSuggestion(
+ elapsedRealtimeMillis, zoneIds);
+ }
+ }
+
+ LocationAlgorithmEvent event = new LocationAlgorithmEvent(algorithmStatus, suggestion);
+ event.addDebugInfo("Command line injection");
+ return event;
+ }
+
+ private static List<String> parseZoneIds(String zoneIdsString) {
+ if ("UNCERTAIN".equals(zoneIdsString)) {
+ return null;
+ } else if ("EMPTY".equals(zoneIdsString)) {
+ return Collections.emptyList();
+ } else {
+ ArrayList<String> zoneIds = new ArrayList<>();
+ StringTokenizer tokenizer = new StringTokenizer(zoneIdsString, ",");
+ while (tokenizer.hasMoreTokens()) {
+ zoneIds.add(tokenizer.nextToken());
+ }
+ return zoneIds;
+ }
+ }
+
+ static void printCommandLineOpts(@NonNull PrintWriter pw) {
+ pw.println("Location algorithm event options:");
+ pw.println(" --status {LocationTimeZoneAlgorithmStatus toString() format}");
+ pw.println(" [--suggestion {UNCERTAIN|EMPTY|<Olson ID>+}]");
+ pw.println();
+ pw.println("See " + LocationAlgorithmEvent.class.getName() + " for more information");
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
index 6c36989320e8..aad53596fc19 100644
--- a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
+++ b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
@@ -89,7 +89,7 @@ public final class MetricsTimeZoneDetectorState {
@NonNull String deviceTimeZoneId,
@Nullable ManualTimeZoneSuggestion latestManualSuggestion,
@Nullable TelephonyTimeZoneSuggestion latestTelephonySuggestion,
- @Nullable GeolocationTimeZoneSuggestion latestGeolocationSuggestion) {
+ @Nullable LocationAlgorithmEvent latestLocationAlgorithmEvent) {
boolean includeZoneIds = configurationInternal.isEnhancedMetricsCollectionEnabled();
String metricDeviceTimeZoneId = includeZoneIds ? deviceTimeZoneId : null;
@@ -101,9 +101,13 @@ public final class MetricsTimeZoneDetectorState {
MetricsTimeZoneSuggestion latestCanonicalTelephonySuggestion =
createMetricsTimeZoneSuggestion(
tzIdOrdinalGenerator, latestTelephonySuggestion, includeZoneIds);
- MetricsTimeZoneSuggestion latestCanonicalGeolocationSuggestion =
- createMetricsTimeZoneSuggestion(
- tzIdOrdinalGenerator, latestGeolocationSuggestion, includeZoneIds);
+
+ MetricsTimeZoneSuggestion latestCanonicalGeolocationSuggestion = null;
+ if (latestLocationAlgorithmEvent != null) {
+ GeolocationTimeZoneSuggestion suggestion = latestLocationAlgorithmEvent.getSuggestion();
+ latestCanonicalGeolocationSuggestion = createMetricsTimeZoneSuggestion(
+ tzIdOrdinalGenerator, suggestion, includeZoneIds);
+ }
return new MetricsTimeZoneDetectorState(
configurationInternal, deviceTimeZoneIdOrdinal, metricDeviceTimeZoneId,
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index 80cf1d6b9031..74a518bf8382 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -59,11 +59,11 @@ public interface TimeZoneDetectorInternal {
boolean setManualTimeZoneForDpm(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion);
/**
- * Suggests the current time zone, determined using geolocation, to the detector. The
- * detector may ignore the signal based on system settings, whether better information is
- * available, and so on. This method may be implemented asynchronously.
+ * Handles the supplied {@link LocationAlgorithmEvent}. The detector may ignore the event based
+ * on system settings, whether better information is available, and so on. This method may be
+ * implemented asynchronously.
*/
- void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion);
+ void handleLocationAlgorithmEvent(@NonNull LocationAlgorithmEvent locationAlgorithmEvent);
/** Generates a state snapshot for metrics. */
@NonNull
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index dfb44df7b993..07d04737c3e2 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -76,13 +76,14 @@ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInter
}
@Override
- public void suggestGeolocationTimeZone(
- @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
- Objects.requireNonNull(timeZoneSuggestion);
+ public void handleLocationAlgorithmEvent(
+ @NonNull LocationAlgorithmEvent locationAlgorithmEvent) {
+ Objects.requireNonNull(locationAlgorithmEvent);
// This call can take place on the mHandler thread because there is no return value.
mHandler.post(
- () -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion));
+ () -> mTimeZoneDetectorStrategy.handleLocationAlgorithmEvent(
+ locationAlgorithmEvent));
}
@Override
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index f415cf03fdec..f8c1c9269ff3 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -300,12 +300,13 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
}
/** Provided for command-line access. This is not exposed as a binder API. */
- void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
+ void handleLocationAlgorithmEvent(@NonNull LocationAlgorithmEvent locationAlgorithmEvent) {
enforceSuggestGeolocationTimeZonePermission();
- Objects.requireNonNull(timeZoneSuggestion);
+ Objects.requireNonNull(locationAlgorithmEvent);
mHandler.post(
- () -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion));
+ () -> mTimeZoneDetectorStrategy.handleLocationAlgorithmEvent(
+ locationAlgorithmEvent));
}
@Override
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
index 1b9f8e6cd66f..69274dba7825 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
@@ -19,6 +19,7 @@ import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_CONFIR
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_DUMP_METRICS;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_GET_TIME_ZONE_STATE;
+import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_HANDLE_LOCATION_ALGORITHM_EVENT;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED;
@@ -27,7 +28,6 @@ import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SERVIC
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SET_GEO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SET_TIME_ZONE_STATE;
-import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE;
import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
@@ -79,8 +79,8 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
return runIsGeoDetectionEnabled();
case SHELL_COMMAND_SET_GEO_DETECTION_ENABLED:
return runSetGeoDetectionEnabled();
- case SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE:
- return runSuggestGeolocationTimeZone();
+ case SHELL_COMMAND_HANDLE_LOCATION_ALGORITHM_EVENT:
+ return runHandleLocationEvent();
case SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE:
return runSuggestManualTimeZone();
case SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE:
@@ -153,34 +153,34 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
return mInterface.updateConfiguration(userId, configuration) ? 0 : 1;
}
- private int runSuggestGeolocationTimeZone() {
- return runSuggestTimeZone(
- () -> GeolocationTimeZoneSuggestion.parseCommandLineArg(this),
- mInterface::suggestGeolocationTimeZone);
+ private int runHandleLocationEvent() {
+ return runSingleArgMethod(
+ () -> LocationAlgorithmEvent.parseCommandLineArg(this),
+ mInterface::handleLocationAlgorithmEvent);
}
private int runSuggestManualTimeZone() {
- return runSuggestTimeZone(
+ return runSingleArgMethod(
() -> ManualTimeZoneSuggestion.parseCommandLineArg(this),
mInterface::suggestManualTimeZone);
}
private int runSuggestTelephonyTimeZone() {
- return runSuggestTimeZone(
+ return runSingleArgMethod(
() -> TelephonyTimeZoneSuggestion.parseCommandLineArg(this),
mInterface::suggestTelephonyTimeZone);
}
- private <T> int runSuggestTimeZone(Supplier<T> suggestionParser, Consumer<T> invoker) {
+ private <T> int runSingleArgMethod(Supplier<T> argParser, Consumer<T> invoker) {
final PrintWriter pw = getOutPrintWriter();
try {
- T suggestion = suggestionParser.get();
- if (suggestion == null) {
- pw.println("Error: suggestion not specified");
+ T arg = argParser.get();
+ if (arg == null) {
+ pw.println("Error: arg not specified");
return 1;
}
- invoker.accept(suggestion);
- pw.println("Suggestion " + suggestion + " injected.");
+ invoker.accept(arg);
+ pw.println("Arg " + arg + " injected.");
return 0;
} catch (RuntimeException e) {
pw.println(e);
@@ -263,18 +263,18 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
pw.printf(" Sets the geolocation time zone detection enabled setting.\n");
pw.printf(" %s\n", SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK);
pw.printf(" Signals that telephony time zone detection fall back can be used if"
- + " geolocation detection is supported and enabled. This is a temporary state until"
- + " geolocation detection becomes \"certain\". To have an effect this requires that"
- + " the telephony fallback feature is supported on the device, see below for"
- + " for device_config flags.\n");
- pw.println();
- pw.printf(" %s <geolocation suggestion opts>\n",
- SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE);
- pw.printf(" Suggests a time zone as if via the \"location\" origin.\n");
+ + " geolocation detection is supported and enabled.\n)");
+ pw.printf(" This is a temporary state until geolocation detection becomes \"certain\"."
+ + "\n");
+ pw.printf(" To have an effect this requires that the telephony fallback feature is"
+ + " supported on the device, see below for device_config flags.\n");
+ pw.printf(" %s <location event opts>\n", SHELL_COMMAND_HANDLE_LOCATION_ALGORITHM_EVENT);
+ pw.printf(" Simulates an event from the location time zone detection algorithm.\n");
pw.printf(" %s <manual suggestion opts>\n", SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE);
- pw.printf(" Suggests a time zone as if via the \"manual\" origin.\n");
+ pw.printf(" Suggests a time zone as if supplied by a user manually.\n");
pw.printf(" %s <telephony suggestion opts>\n", SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE);
- pw.printf(" Suggests a time zone as if via the \"telephony\" origin.\n");
+ pw.printf(" Simulates a time zone suggestion from the telephony time zone detection"
+ + " algorithm.\n");
pw.printf(" %s\n", SHELL_COMMAND_GET_TIME_ZONE_STATE);
pw.printf(" Returns the current time zone setting state.\n");
pw.printf(" %s <time zone state options>\n", SHELL_COMMAND_SET_TIME_ZONE_STATE);
@@ -284,7 +284,7 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
pw.printf(" %s\n", SHELL_COMMAND_DUMP_METRICS);
pw.printf(" Dumps the service metrics to stdout for inspection.\n");
pw.println();
- GeolocationTimeZoneSuggestion.printCommandLineOpts(pw);
+ LocationAlgorithmEvent.printCommandLineOpts(pw);
pw.println();
ManualTimeZoneSuggestion.printCommandLineOpts(pw);
pw.println();
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 328cf72b262e..5768a6bfa0dc 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -157,10 +157,9 @@ public interface TimeZoneDetectorStrategy extends Dumpable {
boolean confirmTimeZone(@NonNull String timeZoneId);
/**
- * Suggests zero, one or more time zones for the device, or withdraws a previous suggestion if
- * {@link GeolocationTimeZoneSuggestion#getZoneIds()} is {@code null}.
+ * Handles an event from the location-based time zone detection algorithm.
*/
- void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion suggestion);
+ void handleLocationAlgorithmEvent(@NonNull LocationAlgorithmEvent event);
/**
* Suggests a time zone for the device using manually-entered (i.e. user sourced) information.
@@ -183,7 +182,7 @@ public interface TimeZoneDetectorStrategy extends Dumpable {
/**
* Tells the strategy that it can fall back to telephony detection while geolocation detection
- * remains uncertain. {@link #suggestGeolocationTimeZone(GeolocationTimeZoneSuggestion)} can
+ * remains uncertain. {@link #handleLocationAlgorithmEvent(LocationAlgorithmEvent)} can
* disable it again. See {@link TimeZoneDetectorStrategy} for details.
*/
void enableTelephonyTimeZoneFallback();
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index ecf25e9c157c..eecf0f74bff6 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -29,9 +29,13 @@ import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.time.DetectorStatusTypes;
+import android.app.time.LocationTimeZoneAlgorithmStatus;
+import android.app.time.TelephonyTimeZoneAlgorithmStatus;
import android.app.time.TimeZoneCapabilities;
import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
+import android.app.time.TimeZoneDetectorStatus;
import android.app.time.TimeZoneState;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
@@ -183,11 +187,10 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
/**
- * The latest geolocation suggestion received. If the user disabled geolocation time zone
- * detection then the latest suggestion is cleared.
+ * The latest location algorithm event received.
*/
@GuardedBy("this")
- private final ReferenceWithHistory<GeolocationTimeZoneSuggestion> mLatestGeoLocationSuggestion =
+ private final ReferenceWithHistory<LocationAlgorithmEvent> mLatestLocationAlgorithmEvent =
new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
/**
@@ -208,6 +211,14 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
@NonNull private final List<StateChangeListener> mStateChangeListeners = new ArrayList<>();
/**
+ * A snapshot of the current detector status. A local copy is cached because it is relatively
+ * heavyweight to obtain and is used more often than it is expected to change.
+ */
+ @GuardedBy("this")
+ @NonNull
+ private TimeZoneDetectorStatus mDetectorStatus;
+
+ /**
* A snapshot of the current user's {@link ConfigurationInternal}. A local copy is cached
* because it is relatively heavyweight to obtain and is used more often than it is expected to
* change. Because many operations are asynchronous, this value may be out of date but should
@@ -258,8 +269,10 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
// Listen for config and user changes and get an initial snapshot of configuration.
StateChangeListener stateChangeListener = this::handleConfigurationInternalMaybeChanged;
mServiceConfigAccessor.addConfigurationInternalChangeListener(stateChangeListener);
- mCurrentConfigurationInternal =
- mServiceConfigAccessor.getCurrentUserConfigurationInternal();
+
+ // Initialize mCurrentConfigurationInternal and mDetectorStatus with their starting
+ // values.
+ updateCurrentConfigurationInternalIfRequired("TimeZoneDetectorStrategyImpl:");
}
}
@@ -278,6 +291,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
configurationInternal = mServiceConfigAccessor.getConfigurationInternal(userId);
}
return new TimeZoneCapabilitiesAndConfig(
+ mDetectorStatus,
configurationInternal.asCapabilities(bypassUserPolicyChecks),
configurationInternal.asConfiguration());
}
@@ -295,28 +309,52 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
// but that could mean an immediate call to getCapabilitiesAndConfig() for the current user
// wouldn't see the update. So, handle the cache update and notifications here. When the
// async update listener triggers it will find everything already up to date and do nothing.
- if (updateSuccessful && mCurrentConfigurationInternal.getUserId() == userId) {
- ConfigurationInternal configurationInternal =
- mServiceConfigAccessor.getConfigurationInternal(userId);
-
- // If the configuration actually changed, update the cached copy synchronously and do
- // other necessary house-keeping / (async) listener notifications.
- if (!configurationInternal.equals(mCurrentConfigurationInternal)) {
- mCurrentConfigurationInternal = configurationInternal;
-
- String logMsg = "updateConfiguration:"
- + " userId=" + userId
- + ", configuration=" + configuration
- + ", bypassUserPolicyChecks=" + bypassUserPolicyChecks
- + ", mCurrentConfigurationInternal=" + mCurrentConfigurationInternal;
- logTimeZoneDebugInfo(logMsg);
-
- handleConfigurationInternalChanged(logMsg);
- }
+ if (updateSuccessful) {
+ String logMsg = "updateConfiguration:"
+ + " userId=" + userId
+ + ", configuration=" + configuration
+ + ", bypassUserPolicyChecks=" + bypassUserPolicyChecks;
+ updateCurrentConfigurationInternalIfRequired(logMsg);
}
return updateSuccessful;
}
+ @GuardedBy("this")
+ private void updateCurrentConfigurationInternalIfRequired(@NonNull String logMsg) {
+ ConfigurationInternal newCurrentConfigurationInternal =
+ mServiceConfigAccessor.getCurrentUserConfigurationInternal();
+ // mCurrentConfigurationInternal is null the first time this method is called.
+ ConfigurationInternal oldCurrentConfigurationInternal = mCurrentConfigurationInternal;
+
+ // If the configuration actually changed, update the cached copy synchronously and do
+ // other necessary house-keeping / (async) listener notifications.
+ if (!newCurrentConfigurationInternal.equals(oldCurrentConfigurationInternal)) {
+ mCurrentConfigurationInternal = newCurrentConfigurationInternal;
+
+ logMsg += " [oldConfiguration=" + oldCurrentConfigurationInternal
+ + ", newConfiguration=" + newCurrentConfigurationInternal
+ + "]";
+ logTimeZoneDebugInfo(logMsg);
+
+ // ConfigurationInternal changes can affect the detector's status.
+ updateDetectorStatus();
+
+ // The configuration and maybe the status changed so notify listeners.
+ notifyStateChangeListenersAsynchronously();
+
+ // The configuration change may have changed available suggestions or the way
+ // suggestions are used, so re-run detection.
+ doAutoTimeZoneDetection(mCurrentConfigurationInternal, logMsg);
+ }
+ }
+
+ @GuardedBy("this")
+ private void notifyStateChangeListenersAsynchronously() {
+ for (StateChangeListener listener : mStateChangeListeners) {
+ mStateChangeHandler.post(listener::onChange);
+ }
+ }
+
@Override
public synchronized void addChangeListener(StateChangeListener listener) {
mStateChangeListeners.add(listener);
@@ -356,33 +394,39 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
}
@Override
- public synchronized void suggestGeolocationTimeZone(
- @NonNull GeolocationTimeZoneSuggestion suggestion) {
-
+ public synchronized void handleLocationAlgorithmEvent(@NonNull LocationAlgorithmEvent event) {
ConfigurationInternal currentUserConfig = mCurrentConfigurationInternal;
if (DBG) {
- Slog.d(LOG_TAG, "Geolocation suggestion received."
+ Slog.d(LOG_TAG, "Location algorithm event received."
+ " currentUserConfig=" + currentUserConfig
- + " newSuggestion=" + suggestion);
+ + " event=" + event);
}
- Objects.requireNonNull(suggestion);
+ Objects.requireNonNull(event);
- // Geolocation suggestions may be stored but not used during time zone detection if the
+ // Location algorithm events may be stored but not used during time zone detection if the
// configuration doesn't have geo time zone detection enabled. The caller is expected to
- // withdraw a previous suggestion (i.e. submit an "uncertain" suggestion, when geo time zone
- // detection is disabled.
-
- // The suggestion's "effective from" time is ignored: we currently assume suggestions
- // are made in a sensible order and the most recent is always the best one to use.
- mLatestGeoLocationSuggestion.set(suggestion);
+ // withdraw a previous suggestion, i.e. submit an event containing an "uncertain"
+ // suggestion, when geo time zone detection is disabled.
+
+ // We currently assume events are made in a sensible order and the most recent is always the
+ // best one to use.
+ mLatestLocationAlgorithmEvent.set(event);
+
+ // The latest location algorithm event can affect the cached detector status, so update it
+ // and notify state change listeners as needed.
+ boolean statusChanged = updateDetectorStatus();
+ if (statusChanged) {
+ notifyStateChangeListenersAsynchronously();
+ }
// Update the mTelephonyTimeZoneFallbackEnabled state if needed: a certain suggestion
// will usually disable telephony fallback mode if it is currently enabled.
+ // TODO(b/236624675)Some provider status codes can be used to enable telephony fallback.
disableTelephonyFallbackIfNeeded();
- // Now perform auto time zone detection. The new suggestion may be used to modify the
- // time zone setting.
- String reason = "New geolocation time zone suggested. suggestion=" + suggestion;
+ // Now perform auto time zone detection. The new event may be used to modify the time zone
+ // setting.
+ String reason = "New location algorithm event received. event=" + event;
doAutoTimeZoneDetection(currentUserConfig, reason);
}
@@ -465,9 +509,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
+ mTelephonyTimeZoneFallbackEnabled;
logTimeZoneDebugInfo(logMsg);
- // mTelephonyTimeZoneFallbackEnabled and mLatestGeoLocationSuggestion interact.
- // If there is currently a certain geolocation suggestion, then the telephony fallback
- // value needs to be considered after changing it.
+ // mTelephonyTimeZoneFallbackEnabled and mLatestLocationAlgorithmEvent interact.
+ // If the latest event contains a "certain" geolocation suggestion, then the telephony
+ // fallback value needs to be considered after changing it.
// With the way that the mTelephonyTimeZoneFallbackEnabled time is currently chosen
// above, and the fact that geolocation suggestions should never have a time in the
// future, the following call will be a no-op, and telephony fallback will remain
@@ -507,7 +551,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
mEnvironment.getDeviceTimeZone(),
getLatestManualSuggestion(),
telephonySuggestion,
- getLatestGeolocationSuggestion());
+ getLatestLocationAlgorithmEvent());
}
@Override
@@ -606,13 +650,15 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
*/
@GuardedBy("this")
private boolean doGeolocationTimeZoneDetection(@NonNull String detectionReason) {
- GeolocationTimeZoneSuggestion latestGeolocationSuggestion =
- mLatestGeoLocationSuggestion.get();
- if (latestGeolocationSuggestion == null) {
+ // Terminate early if there's nothing to do.
+ LocationAlgorithmEvent latestLocationAlgorithmEvent = mLatestLocationAlgorithmEvent.get();
+ if (latestLocationAlgorithmEvent == null
+ || latestLocationAlgorithmEvent.getSuggestion() == null) {
return false;
}
- List<String> zoneIds = latestGeolocationSuggestion.getZoneIds();
+ GeolocationTimeZoneSuggestion suggestion = latestLocationAlgorithmEvent.getSuggestion();
+ List<String> zoneIds = suggestion.getZoneIds();
if (zoneIds == null) {
// This means the originator of the suggestion is uncertain about the time zone. The
// existing time zone setting must be left as it is but detection can go on looking for
@@ -645,13 +691,18 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
}
/**
- * Sets the mTelephonyTimeZoneFallbackEnabled state to {@code false} if the latest geo
- * suggestion is a "certain" suggestion that comes after the time when telephony fallback was
- * enabled.
+ * Sets the mTelephonyTimeZoneFallbackEnabled state to {@code false} if the latest location
+ * algorithm event contains a "certain" suggestion that comes after the time when telephony
+ * fallback was enabled.
*/
@GuardedBy("this")
private void disableTelephonyFallbackIfNeeded() {
- GeolocationTimeZoneSuggestion suggestion = mLatestGeoLocationSuggestion.get();
+ LocationAlgorithmEvent latestLocationAlgorithmEvent = mLatestLocationAlgorithmEvent.get();
+ if (latestLocationAlgorithmEvent == null) {
+ return;
+ }
+
+ GeolocationTimeZoneSuggestion suggestion = latestLocationAlgorithmEvent.getSuggestion();
boolean isLatestSuggestionCertain = suggestion != null && suggestion.getZoneIds() != null;
if (isLatestSuggestionCertain && mTelephonyTimeZoneFallbackEnabled.getValue()) {
// This transition ONLY changes mTelephonyTimeZoneFallbackEnabled from
@@ -809,33 +860,27 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
* Handles a configuration change notification.
*/
private synchronized void handleConfigurationInternalMaybeChanged() {
- ConfigurationInternal currentUserConfig =
- mServiceConfigAccessor.getCurrentUserConfigurationInternal();
-
- // The configuration may not actually have changed so check before doing anything.
- if (!currentUserConfig.equals(mCurrentConfigurationInternal)) {
- String logMsg = "handleConfigurationInternalMaybeChanged:"
- + " oldConfiguration=" + mCurrentConfigurationInternal
- + ", newConfiguration=" + currentUserConfig;
- logTimeZoneDebugInfo(logMsg);
-
- mCurrentConfigurationInternal = currentUserConfig;
-
- handleConfigurationInternalChanged(logMsg);
- }
+ String logMsg = "handleConfigurationInternalMaybeChanged:";
+ updateCurrentConfigurationInternalIfRequired(logMsg);
}
- /** House-keeping that needs to be done when the mCurrentConfigurationInternal has changed. */
+ /**
+ * Called whenever the information that contributes to {@link #mDetectorStatus} could have
+ * changed. Updates the cached status snapshot if required.
+ *
+ * @return true if the status had changed and has been updated
+ */
@GuardedBy("this")
- private void handleConfigurationInternalChanged(@NonNull String logMsg) {
- // Notify change listeners asynchronously.
- for (StateChangeListener listener : mStateChangeListeners) {
- mStateChangeHandler.post(listener::onChange);
+ private boolean updateDetectorStatus() {
+ TimeZoneDetectorStatus newDetectorStatus = createTimeZoneDetectorStatus(
+ mCurrentConfigurationInternal, mLatestLocationAlgorithmEvent.get());
+ // mDetectorStatus is null the first time this method is called.
+ TimeZoneDetectorStatus oldDetectorStatus = mDetectorStatus;
+ boolean statusChanged = !newDetectorStatus.equals(oldDetectorStatus);
+ if (statusChanged) {
+ mDetectorStatus = newDetectorStatus;
}
-
- // The configuration change may have changed available suggestions or the way
- // suggestions are used, so re-run detection.
- doAutoTimeZoneDetection(mCurrentConfigurationInternal, logMsg);
+ return statusChanged;
}
/**
@@ -847,6 +892,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
ipw.increaseIndent(); // level 1
ipw.println("mCurrentConfigurationInternal=" + mCurrentConfigurationInternal);
+ ipw.println("mDetectorStatus=" + mDetectorStatus);
final boolean bypassUserPolicyChecks = false;
ipw.println("[Capabilities="
+ mCurrentConfigurationInternal.asCapabilities(bypassUserPolicyChecks) + "]");
@@ -870,9 +916,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
mLatestManualSuggestion.dump(ipw);
ipw.decreaseIndent(); // level 2
- ipw.println("Geolocation suggestion history:");
+ ipw.println("Location algorithm event history:");
ipw.increaseIndent(); // level 2
- mLatestGeoLocationSuggestion.dump(ipw);
+ mLatestLocationAlgorithmEvent.dump(ipw);
ipw.decreaseIndent(); // level 2
ipw.println("Telephony suggestion history:");
@@ -886,6 +932,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
* A method used to inspect strategy state during tests. Not intended for general use.
*/
@VisibleForTesting
+ @Nullable
public synchronized ManualTimeZoneSuggestion getLatestManualSuggestion() {
return mLatestManualSuggestion.get();
}
@@ -894,6 +941,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
* A method used to inspect strategy state during tests. Not intended for general use.
*/
@VisibleForTesting
+ @Nullable
public synchronized QualifiedTelephonyTimeZoneSuggestion getLatestTelephonySuggestion(
int slotIndex) {
return mTelephonySuggestionsBySlotIndex.get(slotIndex);
@@ -903,8 +951,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
* A method used to inspect strategy state during tests. Not intended for general use.
*/
@VisibleForTesting
- public synchronized GeolocationTimeZoneSuggestion getLatestGeolocationSuggestion() {
- return mLatestGeoLocationSuggestion.get();
+ @Nullable
+ public synchronized LocationAlgorithmEvent getLatestLocationAlgorithmEvent() {
+ return mLatestLocationAlgorithmEvent.get();
}
@VisibleForTesting
@@ -917,6 +966,11 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
return mCurrentConfigurationInternal;
}
+ @VisibleForTesting
+ public synchronized TimeZoneDetectorStatus getCachedDetectorStatusForTests() {
+ return mDetectorStatus;
+ }
+
/**
* A {@link TelephonyTimeZoneSuggestion} with additional qualifying metadata.
*/
@@ -970,4 +1024,42 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
private static String formatDebugString(TimestampedValue<?> value) {
return value.getValue() + " @ " + Duration.ofMillis(value.getReferenceTimeMillis());
}
+
+ @NonNull
+ private static TimeZoneDetectorStatus createTimeZoneDetectorStatus(
+ @NonNull ConfigurationInternal currentConfigurationInternal,
+ @Nullable LocationAlgorithmEvent latestLocationAlgorithmEvent) {
+
+ int detectorStatus;
+ if (!currentConfigurationInternal.isAutoDetectionSupported()) {
+ detectorStatus = DetectorStatusTypes.DETECTOR_STATUS_NOT_SUPPORTED;
+ } else if (currentConfigurationInternal.getAutoDetectionEnabledBehavior()) {
+ detectorStatus = DetectorStatusTypes.DETECTOR_STATUS_RUNNING;
+ } else {
+ detectorStatus = DetectorStatusTypes.DETECTOR_STATUS_NOT_RUNNING;
+ }
+
+ TelephonyTimeZoneAlgorithmStatus telephonyAlgorithmStatus =
+ createTelephonyAlgorithmStatus(currentConfigurationInternal);
+
+ LocationTimeZoneAlgorithmStatus locationAlgorithmStatus =
+ latestLocationAlgorithmEvent == null ? LocationTimeZoneAlgorithmStatus.UNKNOWN
+ : latestLocationAlgorithmEvent.getAlgorithmStatus();
+
+ return new TimeZoneDetectorStatus(
+ detectorStatus, telephonyAlgorithmStatus, locationAlgorithmStatus);
+ }
+
+ @NonNull
+ private static TelephonyTimeZoneAlgorithmStatus createTelephonyAlgorithmStatus(
+ @NonNull ConfigurationInternal currentConfigurationInternal) {
+ int algorithmStatus;
+ if (!currentConfigurationInternal.isTelephonyDetectionSupported()) {
+ algorithmStatus = DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED;
+ } else {
+ // The telephony detector is passive, so we treat it as "running".
+ algorithmStatus = DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
+ }
+ return new TelephonyTimeZoneAlgorithmStatus(algorithmStatus);
+ }
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java
index 1f752f45fd51..e90a1fe34798 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java
@@ -19,7 +19,7 @@ package com.android.server.timezonedetector.location;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.LocationAlgorithmEvent;
import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
import com.android.server.timezonedetector.location.LocationTimeZoneProviderController.State;
@@ -32,14 +32,14 @@ import java.util.Objects;
final class LocationTimeZoneManagerServiceState {
private final @State String mControllerState;
- @Nullable private final GeolocationTimeZoneSuggestion mLastSuggestion;
+ @Nullable private final LocationAlgorithmEvent mLastEvent;
@NonNull private final List<@State String> mControllerStates;
@NonNull private final List<ProviderState> mPrimaryProviderStates;
@NonNull private final List<ProviderState> mSecondaryProviderStates;
LocationTimeZoneManagerServiceState(@NonNull Builder builder) {
mControllerState = builder.mControllerState;
- mLastSuggestion = builder.mLastSuggestion;
+ mLastEvent = builder.mLastEvent;
mControllerStates = Objects.requireNonNull(builder.mControllerStates);
mPrimaryProviderStates = Objects.requireNonNull(builder.mPrimaryProviderStates);
mSecondaryProviderStates = Objects.requireNonNull(builder.mSecondaryProviderStates);
@@ -50,8 +50,8 @@ final class LocationTimeZoneManagerServiceState {
}
@Nullable
- public GeolocationTimeZoneSuggestion getLastSuggestion() {
- return mLastSuggestion;
+ public LocationAlgorithmEvent getLastEvent() {
+ return mLastEvent;
}
@NonNull
@@ -73,7 +73,7 @@ final class LocationTimeZoneManagerServiceState {
public String toString() {
return "LocationTimeZoneManagerServiceState{"
+ "mControllerState=" + mControllerState
- + ", mLastSuggestion=" + mLastSuggestion
+ + ", mLastEvent=" + mLastEvent
+ ", mControllerStates=" + mControllerStates
+ ", mPrimaryProviderStates=" + mPrimaryProviderStates
+ ", mSecondaryProviderStates=" + mSecondaryProviderStates
@@ -83,7 +83,7 @@ final class LocationTimeZoneManagerServiceState {
static final class Builder {
private @State String mControllerState;
- private GeolocationTimeZoneSuggestion mLastSuggestion;
+ private LocationAlgorithmEvent mLastEvent;
private List<@State String> mControllerStates;
private List<ProviderState> mPrimaryProviderStates;
private List<ProviderState> mSecondaryProviderStates;
@@ -95,8 +95,8 @@ final class LocationTimeZoneManagerServiceState {
}
@NonNull
- Builder setLastSuggestion(@NonNull GeolocationTimeZoneSuggestion lastSuggestion) {
- mLastSuggestion = Objects.requireNonNull(lastSuggestion);
+ Builder setLastEvent(@NonNull LocationAlgorithmEvent lastEvent) {
+ mLastEvent = Objects.requireNonNull(lastEvent);
return this;
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
index 60bbea77b636..cefd0b578df8 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
@@ -15,6 +15,10 @@
*/
package com.android.server.timezonedetector.location;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_UNKNOWN;
import static android.app.time.LocationTimeZoneManager.DUMP_STATE_OPTION_PROTO;
import static android.app.time.LocationTimeZoneManager.NULL_PACKAGE_NAME_TOKEN;
import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
@@ -51,9 +55,14 @@ import static com.android.server.timezonedetector.location.LocationTimeZoneProvi
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.time.DetectorStatusTypes.DetectionAlgorithmStatus;
import android.app.time.GeolocationTimeZoneSuggestionProto;
+import android.app.time.LocationTimeZoneAlgorithmStatus;
+import android.app.time.LocationTimeZoneAlgorithmStatusProto;
import android.app.time.LocationTimeZoneManagerProto;
import android.app.time.LocationTimeZoneManagerServiceStateProto;
+import android.app.time.LocationTimeZoneProviderEventProto;
+import android.app.time.TimeZoneDetectorProto;
import android.app.time.TimeZoneProviderStateProto;
import android.app.timezonedetector.TimeZoneDetector;
import android.os.ShellCommand;
@@ -62,6 +71,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.LocationAlgorithmEvent;
import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
import com.android.server.timezonedetector.location.LocationTimeZoneProviderController.State;
@@ -239,19 +249,39 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
outputStream = new DualDumpOutputStream(
new IndentingPrintWriter(getOutPrintWriter(), " "));
}
- if (state.getLastSuggestion() != null) {
- GeolocationTimeZoneSuggestion lastSuggestion = state.getLastSuggestion();
- long lastSuggestionToken = outputStream.start(
- "last_suggestion", LocationTimeZoneManagerServiceStateProto.LAST_SUGGESTION);
- for (String zoneId : lastSuggestion.getZoneIds()) {
- outputStream.write(
- "zone_ids" , GeolocationTimeZoneSuggestionProto.ZONE_IDS, zoneId);
+
+ if (state.getLastEvent() != null) {
+ LocationAlgorithmEvent lastEvent = state.getLastEvent();
+ long lastEventToken = outputStream.start(
+ "last_event", LocationTimeZoneManagerServiceStateProto.LAST_EVENT);
+
+ // lastEvent.algorithmStatus
+ LocationTimeZoneAlgorithmStatus algorithmStatus = lastEvent.getAlgorithmStatus();
+ long algorithmStatusToken = outputStream.start(
+ "algorithm_status", LocationTimeZoneProviderEventProto.ALGORITHM_STATUS);
+ outputStream.write("status", LocationTimeZoneAlgorithmStatusProto.STATUS,
+ convertDetectionAlgorithmStatusToEnumToProtoEnum(algorithmStatus.getStatus()));
+ outputStream.end(algorithmStatusToken);
+
+ // lastEvent.suggestion
+ if (lastEvent.getSuggestion() != null) {
+ long suggestionToken = outputStream.start(
+ "suggestion", LocationTimeZoneProviderEventProto.SUGGESTION);
+ GeolocationTimeZoneSuggestion lastSuggestion = lastEvent.getSuggestion();
+ for (String zoneId : lastSuggestion.getZoneIds()) {
+ outputStream.write(
+ "zone_ids", GeolocationTimeZoneSuggestionProto.ZONE_IDS, zoneId);
+ }
+ outputStream.end(suggestionToken);
}
- for (String debugInfo : lastSuggestion.getDebugInfo()) {
+
+ // lastEvent.debugInfo
+ for (String debugInfo : lastEvent.getDebugInfo()) {
outputStream.write(
- "debug_info", GeolocationTimeZoneSuggestionProto.DEBUG_INFO, debugInfo);
+ "debug_info", LocationTimeZoneProviderEventProto.DEBUG_INFO, debugInfo);
}
- outputStream.end(lastSuggestionToken);
+
+ outputStream.end(lastEventToken);
}
writeControllerStates(outputStream, state.getControllerStates());
@@ -330,6 +360,22 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
}
}
+ private static int convertDetectionAlgorithmStatusToEnumToProtoEnum(
+ @DetectionAlgorithmStatus int statusEnum) {
+ switch (statusEnum) {
+ case DETECTION_ALGORITHM_STATUS_UNKNOWN:
+ return TimeZoneDetectorProto.DETECTION_ALGORITHM_STATUS_UNKNOWN;
+ case DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED:
+ return TimeZoneDetectorProto.DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED;
+ case DETECTION_ALGORITHM_STATUS_NOT_RUNNING:
+ return TimeZoneDetectorProto.DETECTION_ALGORITHM_STATUS_NOT_RUNNING;
+ case DETECTION_ALGORITHM_STATUS_RUNNING:
+ return TimeZoneDetectorProto.DETECTION_ALGORITHM_STATUS_RUNNING;
+ default:
+ throw new IllegalArgumentException("Unknown statusEnum=" + statusEnum);
+ }
+ }
+
private void reportError(@NonNull Throwable e) {
PrintWriter errPrintWriter = getErrPrintWriter();
errPrintWriter.println("Error: ");
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
index b1fc4f561033..15b57b1fbdfb 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
@@ -16,6 +16,10 @@
package com.android.server.timezonedetector.location;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_CERTAIN;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_UNCERTAIN;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_PRESENT;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_READY;
import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
@@ -33,6 +37,7 @@ import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.time.LocationTimeZoneAlgorithmStatus.ProviderStatus;
import android.os.Handler;
import android.os.SystemClock;
import android.service.timezone.TimeZoneProviderEvent;
@@ -295,6 +300,34 @@ abstract class LocationTimeZoneProvider implements Dumpable {
|| stateEnum == PROVIDER_STATE_DESTROYED;
}
+ /**
+ * Maps the internal state enum value to one of the status values exposed to the layers
+ * above.
+ */
+ public @ProviderStatus int getProviderStatus() {
+ switch (stateEnum) {
+ case PROVIDER_STATE_STARTED_INITIALIZING:
+ return PROVIDER_STATUS_NOT_READY;
+ case PROVIDER_STATE_STARTED_CERTAIN:
+ return PROVIDER_STATUS_IS_CERTAIN;
+ case PROVIDER_STATE_STARTED_UNCERTAIN:
+ return PROVIDER_STATUS_IS_UNCERTAIN;
+ case PROVIDER_STATE_PERM_FAILED:
+ // Perm failed means the providers wasn't configured, configured properly,
+ // or has removed itself for other reasons, e.g. turned-down server.
+ return PROVIDER_STATUS_NOT_PRESENT;
+ case PROVIDER_STATE_STOPPED:
+ case PROVIDER_STATE_DESTROYED:
+ // This is a "safe" default that best describes a provider that isn't in one of
+ // the more obviously mapped states.
+ return PROVIDER_STATUS_NOT_READY;
+ case PROVIDER_STATE_UNKNOWN:
+ default:
+ throw new IllegalStateException(
+ "Unknown state enum:" + prettyPrintStateEnum(stateEnum));
+ }
+ }
+
/** Returns the status reported by the provider, if available. */
@Nullable
TimeZoneProviderStatus getReportedStatus() {
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
index a9b9884e0074..ed7ea00ec8f5 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
@@ -35,6 +35,10 @@ import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
+import android.app.time.DetectorStatusTypes;
+import android.app.time.DetectorStatusTypes.DetectionAlgorithmStatus;
+import android.app.time.LocationTimeZoneAlgorithmStatus;
+import android.app.time.LocationTimeZoneAlgorithmStatus.ProviderStatus;
import android.service.timezone.TimeZoneProviderEvent;
import android.service.timezone.TimeZoneProviderSuggestion;
import android.util.IndentingPrintWriter;
@@ -44,6 +48,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.Dumpable;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.LocationAlgorithmEvent;
import com.android.server.timezonedetector.ReferenceWithHistory;
import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue;
@@ -83,8 +88,7 @@ import java.util.Objects;
* <p>All incoming calls except for {@link
* LocationTimeZoneProviderController#dump(android.util.IndentingPrintWriter, String[])} must be
* made on the {@link android.os.Handler} thread of the {@link ThreadingDomain} passed to {@link
- * #LocationTimeZoneProviderController(ThreadingDomain, LocationTimeZoneProvider,
- * LocationTimeZoneProvider)}.
+ * #LocationTimeZoneProviderController}.
*
* <p>Provider / controller integration notes:
*
@@ -172,10 +176,10 @@ class LocationTimeZoneProviderController implements Dumpable {
@GuardedBy("mSharedLock")
private final ReferenceWithHistory<@State String> mState = new ReferenceWithHistory<>(10);
- /** Contains the last suggestion actually made, if there is one. */
+ /** Contains the last event reported, if there is one. */
@GuardedBy("mSharedLock")
@Nullable
- private GeolocationTimeZoneSuggestion mLastSuggestion;
+ private LocationAlgorithmEvent mLastEvent;
LocationTimeZoneProviderController(@NonNull ThreadingDomain threadingDomain,
@NonNull MetricsLogger metricsLogger,
@@ -213,7 +217,7 @@ class LocationTimeZoneProviderController implements Dumpable {
setState(STATE_PROVIDERS_INITIALIZING);
mPrimaryProvider.initialize(providerListener);
mSecondaryProvider.initialize(providerListener);
- setState(STATE_STOPPED);
+ setStateAndReportStatusOnlyEvent(STATE_STOPPED, "initialize()");
alterProvidersStartedStateIfRequired(
null /* oldConfiguration */, mCurrentUserConfiguration);
@@ -273,13 +277,51 @@ class LocationTimeZoneProviderController implements Dumpable {
// Enter destroyed state.
mPrimaryProvider.destroy();
mSecondaryProvider.destroy();
- setState(STATE_DESTROYED);
+ setStateAndReportStatusOnlyEvent(STATE_DESTROYED, "destroy()");
}
}
/**
- * Updates {@link #mState} if needed, and performs all the record-keeping / callbacks associated
- * with state changes.
+ * Sets the state and reports an event containing the algorithm status and a {@code null}
+ * suggestion.
+ */
+ @GuardedBy("mSharedLock")
+ private void setStateAndReportStatusOnlyEvent(@State String state, @NonNull String reason) {
+ setState(state);
+
+ final GeolocationTimeZoneSuggestion suggestion = null;
+ LocationAlgorithmEvent event =
+ new LocationAlgorithmEvent(generateCurrentAlgorithmStatus(), suggestion);
+ event.addDebugInfo(reason);
+ reportEvent(event);
+ }
+
+ /**
+ * Reports an event containing the algorithm status and the supplied suggestion.
+ */
+ @GuardedBy("mSharedLock")
+ private void reportSuggestionEvent(
+ @NonNull GeolocationTimeZoneSuggestion suggestion, @NonNull String reason) {
+ LocationTimeZoneAlgorithmStatus algorithmStatus = generateCurrentAlgorithmStatus();
+ LocationAlgorithmEvent event = new LocationAlgorithmEvent(
+ algorithmStatus, suggestion);
+ event.addDebugInfo(reason);
+ reportEvent(event);
+ }
+
+ /**
+ * Sends an event immediately. This method updates {@link #mLastEvent}.
+ */
+ @GuardedBy("mSharedLock")
+ private void reportEvent(@NonNull LocationAlgorithmEvent event) {
+ debugLog("makeSuggestion: suggestion=" + event);
+ mCallback.sendEvent(event);
+ mLastEvent = event;
+ }
+
+ /**
+ * Updates the state if needed. This includes setting {@link #mState} and performing all the
+ * record-keeping / callbacks associated with state changes.
*/
@GuardedBy("mSharedLock")
private void setState(@State String state) {
@@ -300,17 +342,7 @@ class LocationTimeZoneProviderController implements Dumpable {
// By definition, if both providers are stopped, the controller is uncertain.
cancelUncertaintyTimeout();
- // If a previous "certain" suggestion has been made, then a new "uncertain"
- // suggestion must now be made to indicate the controller {does not / no longer has}
- // an opinion and will not be sending further updates (until at least the providers are
- // re-started).
- if (Objects.equals(mState.get(), STATE_CERTAIN)) {
- GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
- mEnvironment.elapsedRealtimeMillis(),
- "Withdraw previous suggestion, providers are stopping: " + reason);
- makeSuggestion(suggestion, STATE_UNCERTAIN);
- }
- setState(STATE_STOPPED);
+ setStateAndReportStatusOnlyEvent(STATE_STOPPED, "Providers stopped: " + reason);
}
@GuardedBy("mSharedLock")
@@ -381,7 +413,7 @@ class LocationTimeZoneProviderController implements Dumpable {
// timeout started when the primary entered {started uncertain} should be cancelled.
if (newIsGeoDetectionExecutionEnabled) {
- setState(STATE_INITIALIZING);
+ setStateAndReportStatusOnlyEvent(STATE_INITIALIZING, "initializing()");
// Try to start the primary provider.
tryStartProvider(mPrimaryProvider, newConfiguration);
@@ -397,13 +429,11 @@ class LocationTimeZoneProviderController implements Dumpable {
ProviderState newSecondaryState = mSecondaryProvider.getCurrentState();
if (!newSecondaryState.isStarted()) {
// If both providers are {perm failed} then the controller immediately
- // reports uncertain.
- GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
- mEnvironment.elapsedRealtimeMillis(),
- "Providers are failed:"
- + " primary=" + mPrimaryProvider.getCurrentState()
- + " secondary=" + mPrimaryProvider.getCurrentState());
- makeSuggestion(suggestion, STATE_FAILED);
+ // reports the failure.
+ String reason = "Providers are failed:"
+ + " primary=" + mPrimaryProvider.getCurrentState()
+ + " secondary=" + mPrimaryProvider.getCurrentState();
+ setStateAndReportStatusOnlyEvent(STATE_FAILED, reason);
}
}
} else {
@@ -537,12 +567,10 @@ class LocationTimeZoneProviderController implements Dumpable {
// If both providers are now terminated, then a suggestion must be sent informing the
// time zone detector that there are no further updates coming in the future.
- GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
- mEnvironment.elapsedRealtimeMillis(),
- "Both providers are terminated:"
- + " primary=" + primaryCurrentState.provider
- + ", secondary=" + secondaryCurrentState.provider);
- makeSuggestion(suggestion, STATE_FAILED);
+ String reason = "Both providers are terminated:"
+ + " primary=" + primaryCurrentState.provider
+ + ", secondary=" + secondaryCurrentState.provider;
+ setStateAndReportStatusOnlyEvent(STATE_FAILED, reason);
}
}
@@ -615,6 +643,9 @@ class LocationTimeZoneProviderController implements Dumpable {
TimeZoneProviderSuggestion providerSuggestion = providerEvent.getSuggestion();
+ // Set the current state so it is correct when the suggestion event is created.
+ setState(STATE_CERTAIN);
+
// For the suggestion's effectiveFromElapsedMillis, use the time embedded in the provider's
// suggestion (which indicates the time when the provider detected the location used to
// establish the time zone).
@@ -623,15 +654,13 @@ class LocationTimeZoneProviderController implements Dumpable {
// this would hinder the ability for the time_zone_detector to judge which suggestions are
// based on newer information when comparing suggestions between different sources.
long effectiveFromElapsedMillis = providerSuggestion.getElapsedRealtimeMillis();
- GeolocationTimeZoneSuggestion geoSuggestion =
+ GeolocationTimeZoneSuggestion suggestion =
GeolocationTimeZoneSuggestion.createCertainSuggestion(
effectiveFromElapsedMillis, providerSuggestion.getTimeZoneIds());
-
- String debugInfo = "Event received provider=" + provider
+ String debugInfo = "Provider event received: provider=" + provider
+ ", providerEvent=" + providerEvent
+ ", suggestionCreationTime=" + mEnvironment.elapsedRealtimeMillis();
- geoSuggestion.addDebugInfo(debugInfo);
- makeSuggestion(geoSuggestion, STATE_CERTAIN);
+ reportSuggestionEvent(suggestion, debugInfo);
}
@Override
@@ -647,7 +676,7 @@ class LocationTimeZoneProviderController implements Dumpable {
+ mEnvironment.getProviderInitializationTimeoutFuzz());
ipw.println("uncertaintyDelay=" + mEnvironment.getUncertaintyDelay());
ipw.println("mState=" + mState.get());
- ipw.println("mLastSuggestion=" + mLastSuggestion);
+ ipw.println("mLastEvent=" + mLastEvent);
ipw.println("State history:");
ipw.increaseIndent(); // level 2
@@ -668,19 +697,6 @@ class LocationTimeZoneProviderController implements Dumpable {
}
}
- /**
- * Sends an immediate suggestion and enters a new state if needed. This method updates
- * mLastSuggestion and changes mStateEnum / reports the new state for metrics.
- */
- @GuardedBy("mSharedLock")
- private void makeSuggestion(@NonNull GeolocationTimeZoneSuggestion suggestion,
- @State String newState) {
- debugLog("makeSuggestion: suggestion=" + suggestion);
- mCallback.suggest(suggestion);
- mLastSuggestion = suggestion;
- setState(newState);
- }
-
/** Clears the uncertainty timeout. */
@GuardedBy("mSharedLock")
private void cancelUncertaintyTimeout() {
@@ -688,18 +704,16 @@ class LocationTimeZoneProviderController implements Dumpable {
}
/**
- * Called when a provider has become "uncertain" about the time zone.
+ * Called when a provider has reported it is "uncertain" about the time zone.
*
* <p>A provider is expected to report its uncertainty as soon as it becomes uncertain, as
* this enables the most flexibility for the controller to start other providers when there are
- * multiple ones available. The controller is therefore responsible for deciding when to make a
- * "uncertain" suggestion to the downstream time zone detector.
+ * multiple ones available. The controller is therefore responsible for deciding when to pass
+ * the "uncertain" suggestion to the downstream time zone detector.
*
* <p>This method schedules an "uncertainty" timeout (if one isn't already scheduled) to be
* triggered later if nothing else preempts it. It can be preempted if the provider becomes
- * certain (or does anything else that calls {@link
- * #makeSuggestion(GeolocationTimeZoneSuggestion, String)}) within {@link
- * Environment#getUncertaintyDelay()}. Preemption causes the scheduled
+ * certain within {@link Environment#getUncertaintyDelay()}. Preemption causes the scheduled
* "uncertainty" timeout to be cancelled. If the provider repeatedly sends uncertainty events
* within the uncertainty delay period, those events are effectively ignored (i.e. the timeout
* is not reset each time).
@@ -741,6 +755,8 @@ class LocationTimeZoneProviderController implements Dumpable {
synchronized (mSharedLock) {
long afterUncertaintyTimeoutElapsedMillis = mEnvironment.elapsedRealtimeMillis();
+ setState(STATE_UNCERTAIN);
+
// For the effectiveFromElapsedMillis suggestion property, use the
// uncertaintyStartedElapsedMillis. This is the time when the provider first reported
// uncertainty, i.e. before the uncertainty timeout.
@@ -749,30 +765,65 @@ class LocationTimeZoneProviderController implements Dumpable {
// the location_time_zone_manager finally confirms that the time zone was uncertain,
// but the suggestion property allows the information to be back-dated, which should
// help when comparing suggestions from different sources.
- GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
- uncertaintyStartedElapsedMillis,
- "Uncertainty timeout triggered for " + provider.getName() + ":"
- + " primary=" + mPrimaryProvider
- + ", secondary=" + mSecondaryProvider
- + ", uncertaintyStarted="
- + Duration.ofMillis(uncertaintyStartedElapsedMillis)
- + ", afterUncertaintyTimeout="
- + Duration.ofMillis(afterUncertaintyTimeoutElapsedMillis)
- + ", uncertaintyDelay=" + uncertaintyDelay
- );
- makeSuggestion(suggestion, STATE_UNCERTAIN);
+ GeolocationTimeZoneSuggestion suggestion =
+ GeolocationTimeZoneSuggestion.createUncertainSuggestion(
+ uncertaintyStartedElapsedMillis);
+ String debugInfo = "Uncertainty timeout triggered for " + provider.getName() + ":"
+ + " primary=" + mPrimaryProvider
+ + ", secondary=" + mSecondaryProvider
+ + ", uncertaintyStarted="
+ + Duration.ofMillis(uncertaintyStartedElapsedMillis)
+ + ", afterUncertaintyTimeout="
+ + Duration.ofMillis(afterUncertaintyTimeoutElapsedMillis)
+ + ", uncertaintyDelay=" + uncertaintyDelay;
+ reportSuggestionEvent(suggestion, debugInfo);
}
}
+ @GuardedBy("mSharedLock")
@NonNull
- private static GeolocationTimeZoneSuggestion createUncertainSuggestion(
- @ElapsedRealtimeLong long effectiveFromElapsedMillis,
- @NonNull String reason) {
- GeolocationTimeZoneSuggestion suggestion =
- GeolocationTimeZoneSuggestion.createUncertainSuggestion(
- effectiveFromElapsedMillis);
- suggestion.addDebugInfo(reason);
- return suggestion;
+ private LocationTimeZoneAlgorithmStatus generateCurrentAlgorithmStatus() {
+ @State String controllerState = mState.get();
+ ProviderState primaryProviderState = mPrimaryProvider.getCurrentState();
+ ProviderState secondaryProviderState = mSecondaryProvider.getCurrentState();
+ return createAlgorithmStatus(controllerState, primaryProviderState, secondaryProviderState);
+ }
+
+ @NonNull
+ private static LocationTimeZoneAlgorithmStatus createAlgorithmStatus(
+ @NonNull @State String controllerState,
+ @NonNull ProviderState primaryProviderState,
+ @NonNull ProviderState secondaryProviderState) {
+
+ @DetectionAlgorithmStatus int algorithmStatus =
+ mapControllerStateToDetectionAlgorithmStatus(controllerState);
+ @ProviderStatus int primaryProviderStatus = primaryProviderState.getProviderStatus();
+ @ProviderStatus int secondaryProviderStatus = secondaryProviderState.getProviderStatus();
+
+ // Neither provider is running. The algorithm is not running.
+ return new LocationTimeZoneAlgorithmStatus(algorithmStatus,
+ primaryProviderStatus, primaryProviderState.getReportedStatus(),
+ secondaryProviderStatus, secondaryProviderState.getReportedStatus());
+ }
+
+ /**
+ * Maps the internal state enum value to one of the status values exposed to the layers above.
+ */
+ private static @DetectionAlgorithmStatus int mapControllerStateToDetectionAlgorithmStatus(
+ @NonNull @State String controllerState) {
+ switch (controllerState) {
+ case STATE_INITIALIZING:
+ case STATE_PROVIDERS_INITIALIZING:
+ case STATE_CERTAIN:
+ case STATE_UNCERTAIN:
+ return DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
+ case STATE_STOPPED:
+ case STATE_DESTROYED:
+ case STATE_FAILED:
+ case STATE_UNKNOWN:
+ default:
+ return DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_RUNNING;
+ }
}
/**
@@ -798,8 +849,8 @@ class LocationTimeZoneProviderController implements Dumpable {
synchronized (mSharedLock) {
LocationTimeZoneManagerServiceState.Builder builder =
new LocationTimeZoneManagerServiceState.Builder();
- if (mLastSuggestion != null) {
- builder.setLastSuggestion(mLastSuggestion);
+ if (mLastEvent != null) {
+ builder.setLastEvent(mLastEvent);
}
builder.setControllerState(mState.get())
.setStateChanges(mRecordedStates)
@@ -867,17 +918,15 @@ class LocationTimeZoneProviderController implements Dumpable {
abstract static class Callback {
@NonNull protected final ThreadingDomain mThreadingDomain;
- @NonNull protected final Object mSharedLock;
Callback(@NonNull ThreadingDomain threadingDomain) {
mThreadingDomain = Objects.requireNonNull(threadingDomain);
- mSharedLock = threadingDomain.getLockObject();
}
/**
* Suggests the latest time zone state for the device.
*/
- abstract void suggest(@NonNull GeolocationTimeZoneSuggestion suggestion);
+ abstract void sendEvent(@NonNull LocationAlgorithmEvent event);
}
/**
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerCallbackImpl.java
index 0c751aaa62c7..7eb7e01b539a 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerCallbackImpl.java
@@ -19,7 +19,7 @@ package com.android.server.timezonedetector.location;
import android.annotation.NonNull;
import com.android.server.LocalServices;
-import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.LocationAlgorithmEvent;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
/**
@@ -34,11 +34,11 @@ class LocationTimeZoneProviderControllerCallbackImpl
}
@Override
- void suggest(@NonNull GeolocationTimeZoneSuggestion suggestion) {
+ void sendEvent(@NonNull LocationAlgorithmEvent event) {
mThreadingDomain.assertCurrentThread();
TimeZoneDetectorInternal timeZoneDetector =
LocalServices.getService(TimeZoneDetectorInternal.class);
- timeZoneDetector.suggestGeolocationTimeZone(suggestion);
+ timeZoneDetector.handleLocationAlgorithmEvent(event);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index fed8b4040aba..bcdc65c19330 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -21,12 +21,14 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
+import android.app.time.TimeZoneDetectorStatus;
import android.app.time.TimeZoneState;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.util.IndentingPrintWriter;
import java.util.ArrayList;
+import java.util.Objects;
public class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
@@ -34,14 +36,17 @@ public class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
new FakeServiceConfigAccessor();
private final ArrayList<StateChangeListener> mListeners = new ArrayList<>();
private TimeZoneState mTimeZoneState;
+ private TimeZoneDetectorStatus mStatus;
public FakeTimeZoneDetectorStrategy() {
mFakeServiceConfigAccessor.addConfigurationInternalChangeListener(
this::notifyChangeListeners);
}
- public void initializeConfiguration(ConfigurationInternal configuration) {
+ public void initializeConfigurationAndStatus(
+ ConfigurationInternal configuration, TimeZoneDetectorStatus status) {
mFakeServiceConfigAccessor.initializeCurrentUserConfiguration(configuration);
+ mStatus = Objects.requireNonNull(status);
}
@Override
@@ -57,6 +62,7 @@ public class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
assertEquals("Multi-user testing not supported",
configurationInternal.getUserId(), userId);
return new TimeZoneCapabilitiesAndConfig(
+ mStatus,
configurationInternal.asCapabilities(bypassUserPolicyChecks),
configurationInternal.asConfiguration());
}
@@ -90,7 +96,7 @@ public class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
}
@Override
- public void suggestGeolocationTimeZone(GeolocationTimeZoneSuggestion timeZoneSuggestion) {
+ public void handleLocationAlgorithmEvent(LocationAlgorithmEvent locationAlgorithmEvent) {
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
index 0f667b3a690b..602842addff2 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
@@ -16,13 +16,8 @@
package com.android.server.timezonedetector;
-import static com.android.server.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNull;
-
-import android.os.ShellCommand;
import org.junit.Test;
@@ -49,11 +44,6 @@ public class GeolocationTimeZoneSuggestionTest {
assertEquals(certain1v1, certain1v2);
assertEquals(certain1v2, certain1v1);
- // DebugInfo must not be considered in equals().
- certain1v1.addDebugInfo("Debug info 1");
- certain1v2.addDebugInfo("Debug info 2");
- assertEquals(certain1v1, certain1v2);
-
long time2 = 2222L;
GeolocationTimeZoneSuggestion certain2 =
GeolocationTimeZoneSuggestion.createCertainSuggestion(time2, ARBITRARY_ZONE_IDS1);
@@ -71,40 +61,4 @@ public class GeolocationTimeZoneSuggestionTest {
assertNotEquals(certain1v1, certain3);
assertNotEquals(certain3, certain1v1);
}
-
- @Test(expected = IllegalArgumentException.class)
- public void testParseCommandLineArg_noZoneIdsArg() {
- ShellCommand testShellCommand =
- createShellCommandWithArgsAndOptions(Collections.emptyList());
- GeolocationTimeZoneSuggestion.parseCommandLineArg(testShellCommand);
- }
-
- @Test
- public void testParseCommandLineArg_zoneIdsUncertain() {
- ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
- "--zone_ids UNCERTAIN");
- assertNull(GeolocationTimeZoneSuggestion.parseCommandLineArg(testShellCommand)
- .getZoneIds());
- }
-
- @Test
- public void testParseCommandLineArg_zoneIdsEmpty() {
- ShellCommand testShellCommand = createShellCommandWithArgsAndOptions("--zone_ids EMPTY");
- assertEquals(Collections.emptyList(),
- GeolocationTimeZoneSuggestion.parseCommandLineArg(testShellCommand).getZoneIds());
- }
-
- @Test
- public void testParseCommandLineArg_zoneIdsPresent() {
- ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
- "--zone_ids Europe/London,Europe/Paris");
- assertEquals(Arrays.asList("Europe/London", "Europe/Paris"),
- GeolocationTimeZoneSuggestion.parseCommandLineArg(testShellCommand).getZoneIds());
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testParseCommandLineArg_unknownArgument() {
- ShellCommand testShellCommand = createShellCommandWithArgsAndOptions("--bad_arg 0");
- GeolocationTimeZoneSuggestion.parseCommandLineArg(testShellCommand);
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java
new file mode 100644
index 000000000000..4c14014405f4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 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 com.android.server.timezonedetector;
+
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_CERTAIN;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_PRESENT;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_READY;
+import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_NOT_APPLICABLE;
+import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK;
+import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_OK;
+
+import static com.android.server.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.app.time.LocationTimeZoneAlgorithmStatus;
+import android.os.ShellCommand;
+import android.service.timezone.TimeZoneProviderStatus;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+public class LocationAlgorithmEventTest {
+
+ public static final TimeZoneProviderStatus ARBITRARY_PROVIDER_STATUS =
+ new TimeZoneProviderStatus.Builder()
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_NOT_APPLICABLE)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
+ .build();
+
+ public static final LocationTimeZoneAlgorithmStatus ARBITRARY_LOCATION_ALGORITHM_STATUS =
+ new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_IS_CERTAIN, ARBITRARY_PROVIDER_STATUS,
+ PROVIDER_STATUS_NOT_PRESENT, null);
+
+ @Test
+ public void testEquals() {
+ GeolocationTimeZoneSuggestion suggestion1 =
+ GeolocationTimeZoneSuggestion.createUncertainSuggestion(1111L);
+ LocationTimeZoneAlgorithmStatus status1 = new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_NOT_PRESENT, null, PROVIDER_STATUS_NOT_PRESENT, null);
+ LocationAlgorithmEvent event1v1 = new LocationAlgorithmEvent(status1, suggestion1);
+ assertEqualsAndHashCode(event1v1, event1v1);
+
+ LocationAlgorithmEvent event1v2 = new LocationAlgorithmEvent(status1, suggestion1);
+ assertEqualsAndHashCode(event1v1, event1v2);
+
+ GeolocationTimeZoneSuggestion suggestion2 =
+ GeolocationTimeZoneSuggestion.createUncertainSuggestion(2222L);
+ LocationAlgorithmEvent event2 = new LocationAlgorithmEvent(status1, suggestion2);
+ assertNotEquals(event1v1, event2);
+
+ LocationTimeZoneAlgorithmStatus status2 = new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_NOT_PRESENT, null, PROVIDER_STATUS_NOT_READY, null);
+ LocationAlgorithmEvent event3 = new LocationAlgorithmEvent(status2, suggestion1);
+ assertNotEquals(event1v1, event3);
+
+ // DebugInfo must not be considered in equals().
+ event1v1.addDebugInfo("Debug info 1");
+ event1v2.addDebugInfo("Debug info 2");
+ assertEquals(event1v1, event1v2);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseCommandLineArg_noStatus() {
+ GeolocationTimeZoneSuggestion suggestion =
+ GeolocationTimeZoneSuggestion.createUncertainSuggestion(1111L);
+ ShellCommand testShellCommand =
+ createShellCommandWithArgsAndOptions(
+ Arrays.asList("--suggestion", suggestion.toString()));
+
+ LocationAlgorithmEvent.parseCommandLineArg(testShellCommand);
+ }
+
+ @Test
+ public void testParseCommandLineArg_noSuggestion() {
+ GeolocationTimeZoneSuggestion suggestion = null;
+ LocationAlgorithmEvent event = new LocationAlgorithmEvent(
+ ARBITRARY_LOCATION_ALGORITHM_STATUS, suggestion);
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ Arrays.asList("--status", event.getAlgorithmStatus().toString()));
+
+ assertEquals(event, LocationAlgorithmEvent.parseCommandLineArg(testShellCommand));
+ }
+
+ @Test
+ public void testParseCommandLineArg_suggestionUncertain() {
+ GeolocationTimeZoneSuggestion suggestion =
+ GeolocationTimeZoneSuggestion.createUncertainSuggestion(1111L);
+ LocationAlgorithmEvent event = new LocationAlgorithmEvent(
+ ARBITRARY_LOCATION_ALGORITHM_STATUS, suggestion);
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ Arrays.asList("--status", event.getAlgorithmStatus().toString(),
+ "--suggestion", "UNCERTAIN"));
+
+ LocationAlgorithmEvent parsedEvent =
+ LocationAlgorithmEvent.parseCommandLineArg(testShellCommand);
+ assertEquals(event.getAlgorithmStatus(), parsedEvent.getAlgorithmStatus());
+ assertEquals(event.getSuggestion().getZoneIds(), parsedEvent.getSuggestion().getZoneIds());
+ }
+
+ @Test
+ public void testParseCommandLineArg_suggestionEmpty() {
+ GeolocationTimeZoneSuggestion suggestion =
+ GeolocationTimeZoneSuggestion.createCertainSuggestion(
+ 1111L, Collections.emptyList());
+ LocationAlgorithmEvent event = new LocationAlgorithmEvent(
+ ARBITRARY_LOCATION_ALGORITHM_STATUS, suggestion);
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ Arrays.asList("--status", event.getAlgorithmStatus().toString(),
+ "--suggestion", "EMPTY"));
+
+ LocationAlgorithmEvent parsedEvent =
+ LocationAlgorithmEvent.parseCommandLineArg(testShellCommand);
+ assertEquals(event.getAlgorithmStatus(), parsedEvent.getAlgorithmStatus());
+ assertEquals(event.getSuggestion().getZoneIds(), parsedEvent.getSuggestion().getZoneIds());
+ }
+
+ @Test
+ public void testParseCommandLineArg_suggestionPresent() {
+ GeolocationTimeZoneSuggestion suggestion =
+ GeolocationTimeZoneSuggestion.createCertainSuggestion(
+ 1111L, Arrays.asList("Europe/London", "Europe/Paris"));
+ LocationAlgorithmEvent event = new LocationAlgorithmEvent(
+ ARBITRARY_LOCATION_ALGORITHM_STATUS, suggestion);
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ Arrays.asList("--status", event.getAlgorithmStatus().toString(),
+ "--suggestion", "Europe/London,Europe/Paris"));
+
+ LocationAlgorithmEvent parsedEvent =
+ LocationAlgorithmEvent.parseCommandLineArg(testShellCommand);
+ assertEquals(event.getAlgorithmStatus(), parsedEvent.getAlgorithmStatus());
+ assertEquals(event.getSuggestion().getZoneIds(), parsedEvent.getSuggestion().getZoneIds());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseCommandLineArg_unknownArgument() {
+ GeolocationTimeZoneSuggestion suggestion =
+ GeolocationTimeZoneSuggestion.createCertainSuggestion(
+ 1111L, Arrays.asList("Europe/London", "Europe/Paris"));
+ LocationAlgorithmEvent event = new LocationAlgorithmEvent(
+ ARBITRARY_LOCATION_ALGORITHM_STATUS, suggestion);
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ Arrays.asList("--status", event.getAlgorithmStatus().toString(),
+ "--suggestion", "Europe/London,Europe/Paris", "--bad_arg"));
+ LocationAlgorithmEvent.parseCommandLineArg(testShellCommand);
+ }
+
+ private static void assertEqualsAndHashCode(Object one, Object two) {
+ assertEquals(one, two);
+ assertEquals(two, one);
+ assertEquals(one.hashCode(), two.hashCode());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java
index 223c53233065..ea801e887c4c 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java
@@ -16,6 +16,10 @@
package com.android.server.timezonedetector;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_CERTAIN;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_PRESENT;
+
import static com.android.server.timezonedetector.MetricsTimeZoneDetectorState.DETECTION_MODE_GEO;
import static org.junit.Assert.assertEquals;
@@ -23,6 +27,7 @@ import static org.junit.Assert.assertNull;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.UserIdInt;
+import android.app.time.LocationTimeZoneAlgorithmStatus;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
@@ -31,6 +36,7 @@ import com.android.server.timezonedetector.MetricsTimeZoneDetectorState.MetricsT
import org.junit.Test;
import java.util.Arrays;
+import java.util.List;
import java.util.function.Function;
/** Tests for {@link MetricsTimeZoneDetectorState}. */
@@ -38,6 +44,9 @@ public class MetricsTimeZoneDetectorStateTest {
private static final @UserIdInt int ARBITRARY_USER_ID = 1;
private static final @ElapsedRealtimeLong long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234L;
+ private static final LocationTimeZoneAlgorithmStatus ARBITRARY_CERTAIN_STATUS =
+ new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_IS_CERTAIN, null, PROVIDER_STATUS_NOT_PRESENT, null);
private static final String DEVICE_TIME_ZONE_ID = "DeviceTimeZoneId";
private static final ManualTimeZoneSuggestion MANUAL_TIME_ZONE_SUGGESTION =
@@ -50,11 +59,14 @@ public class MetricsTimeZoneDetectorStateTest {
.setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE)
.build();
- private static final GeolocationTimeZoneSuggestion GEOLOCATION_TIME_ZONE_SUGGESTION =
+ public static final GeolocationTimeZoneSuggestion GEOLOCATION_SUGGESTION_CERTAIN =
GeolocationTimeZoneSuggestion.createCertainSuggestion(
ARBITRARY_ELAPSED_REALTIME_MILLIS,
Arrays.asList("GeoTimeZoneId1", "GeoTimeZoneId2"));
+ private static final LocationAlgorithmEvent LOCATION_ALGORITHM_EVENT =
+ new LocationAlgorithmEvent(ARBITRARY_CERTAIN_STATUS, GEOLOCATION_SUGGESTION_CERTAIN);
+
private final OrdinalGenerator<String> mOrdinalGenerator =
new OrdinalGenerator<>(Function.identity());
@@ -68,7 +80,7 @@ public class MetricsTimeZoneDetectorStateTest {
MetricsTimeZoneDetectorState metricsTimeZoneDetectorState =
MetricsTimeZoneDetectorState.create(mOrdinalGenerator, configurationInternal,
DEVICE_TIME_ZONE_ID, MANUAL_TIME_ZONE_SUGGESTION,
- TELEPHONY_TIME_ZONE_SUGGESTION, GEOLOCATION_TIME_ZONE_SUGGESTION);
+ TELEPHONY_TIME_ZONE_SUGGESTION, LOCATION_ALGORITHM_EVENT);
// Assert the content.
assertCommonConfiguration(configurationInternal, metricsTimeZoneDetectorState);
@@ -88,9 +100,10 @@ public class MetricsTimeZoneDetectorStateTest {
assertEquals(expectedTelephonySuggestion,
metricsTimeZoneDetectorState.getLatestTelephonySuggestion());
+ List<String> expectedZoneIds = LOCATION_ALGORITHM_EVENT.getSuggestion().getZoneIds();
MetricsTimeZoneSuggestion expectedGeoSuggestion =
MetricsTimeZoneSuggestion.createCertain(
- GEOLOCATION_TIME_ZONE_SUGGESTION.getZoneIds().toArray(new String[0]),
+ expectedZoneIds.toArray(new String[0]),
new int[] { 3, 4 });
assertEquals(expectedGeoSuggestion,
metricsTimeZoneDetectorState.getLatestGeolocationSuggestion());
@@ -106,7 +119,7 @@ public class MetricsTimeZoneDetectorStateTest {
MetricsTimeZoneDetectorState metricsTimeZoneDetectorState =
MetricsTimeZoneDetectorState.create(mOrdinalGenerator, configurationInternal,
DEVICE_TIME_ZONE_ID, MANUAL_TIME_ZONE_SUGGESTION,
- TELEPHONY_TIME_ZONE_SUGGESTION, GEOLOCATION_TIME_ZONE_SUGGESTION);
+ TELEPHONY_TIME_ZONE_SUGGESTION, LOCATION_ALGORITHM_EVENT);
// Assert the content.
assertCommonConfiguration(configurationInternal, metricsTimeZoneDetectorState);
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
index 8909832391a4..a02c8ca001ce 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
@@ -16,14 +16,22 @@
package com.android.server.timezonedetector;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTOR_STATUS_RUNNING;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_CERTAIN;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_PRESENT;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.app.time.LocationTimeZoneAlgorithmStatus;
+import android.app.time.TelephonyTimeZoneAlgorithmStatus;
import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
+import android.app.time.TimeZoneDetectorStatus;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.content.Context;
import android.os.HandlerThread;
@@ -41,6 +49,15 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
public class TimeZoneDetectorInternalImplTest {
+ private static final TelephonyTimeZoneAlgorithmStatus ARBITRARY_TELEPHONY_STATUS =
+ new TelephonyTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING);
+ private static final LocationTimeZoneAlgorithmStatus ARBITRARY_LOCATION_CERTAIN_STATUS =
+ new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_IS_CERTAIN, null, PROVIDER_STATUS_NOT_PRESENT, null);
+ private static final TimeZoneDetectorStatus ARBITRARY_DETECTOR_STATUS =
+ new TimeZoneDetectorStatus(DETECTOR_STATUS_RUNNING, ARBITRARY_TELEPHONY_STATUS,
+ ARBITRARY_LOCATION_CERTAIN_STATUS);
+
private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234L;
private static final String ARBITRARY_ZONE_ID = "TestZoneId";
private static final List<String> ARBITRARY_ZONE_IDS = Arrays.asList(ARBITRARY_ZONE_ID);
@@ -81,7 +98,8 @@ public class TimeZoneDetectorInternalImplTest {
public void testGetCapabilitiesAndConfigForDpm() throws Exception {
final boolean autoDetectionEnabled = true;
ConfigurationInternal testConfig = createConfigurationInternal(autoDetectionEnabled);
- mFakeTimeZoneDetectorStrategySpy.initializeConfiguration(testConfig);
+ TimeZoneDetectorStatus testStatus = ARBITRARY_DETECTOR_STATUS;
+ mFakeTimeZoneDetectorStrategySpy.initializeConfigurationAndStatus(testConfig, testStatus);
TimeZoneCapabilitiesAndConfig actualCapabilitiesAndConfig =
mTimeZoneDetectorInternal.getCapabilitiesAndConfigForDpm();
@@ -93,6 +111,7 @@ public class TimeZoneDetectorInternalImplTest {
TimeZoneCapabilitiesAndConfig expectedCapabilitiesAndConfig =
new TimeZoneCapabilitiesAndConfig(
+ testStatus,
testConfig.asCapabilities(expectedBypassUserPolicyChecks),
testConfig.asConfiguration());
assertEquals(expectedCapabilitiesAndConfig, actualCapabilitiesAndConfig);
@@ -103,7 +122,9 @@ public class TimeZoneDetectorInternalImplTest {
final boolean autoDetectionEnabled = false;
ConfigurationInternal initialConfigurationInternal =
createConfigurationInternal(autoDetectionEnabled);
- mFakeTimeZoneDetectorStrategySpy.initializeConfiguration(initialConfigurationInternal);
+ TimeZoneDetectorStatus testStatus = ARBITRARY_DETECTOR_STATUS;
+ mFakeTimeZoneDetectorStrategySpy.initializeConfigurationAndStatus(
+ initialConfigurationInternal, testStatus);
TimeZoneConfiguration timeConfiguration = new TimeZoneConfiguration.Builder()
.setAutoDetectionEnabled(true)
@@ -131,13 +152,15 @@ public class TimeZoneDetectorInternalImplTest {
}
@Test
- public void testSuggestGeolocationTimeZone() throws Exception {
+ public void testHandleLocationAlgorithmEvent() throws Exception {
GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion();
- mTimeZoneDetectorInternal.suggestGeolocationTimeZone(timeZoneSuggestion);
+ LocationAlgorithmEvent suggestionEvent = new LocationAlgorithmEvent(
+ ARBITRARY_LOCATION_CERTAIN_STATUS, timeZoneSuggestion);
+ mTimeZoneDetectorInternal.handleLocationAlgorithmEvent(suggestionEvent);
mTestHandler.assertTotalMessagesEnqueued(1);
mTestHandler.waitForMessagesToBeProcessed();
- verify(mFakeTimeZoneDetectorStrategySpy).suggestGeolocationTimeZone(timeZoneSuggestion);
+ verify(mFakeTimeZoneDetectorStrategySpy).handleLocationAlgorithmEvent(suggestionEvent);
}
private static ManualTimeZoneSuggestion createManualTimeZoneSuggestion() {
return new ManualTimeZoneSuggestion(ARBITRARY_ZONE_ID);
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index d8346ee4355b..d9d8053e6220 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -16,6 +16,11 @@
package com.android.server.timezonedetector;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTOR_STATUS_RUNNING;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_CERTAIN;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_PRESENT;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
@@ -34,8 +39,11 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.time.ITimeZoneDetectorListener;
+import android.app.time.LocationTimeZoneAlgorithmStatus;
+import android.app.time.TelephonyTimeZoneAlgorithmStatus;
import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
+import android.app.time.TimeZoneDetectorStatus;
import android.app.time.TimeZoneState;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
@@ -59,6 +67,13 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
public class TimeZoneDetectorServiceTest {
+ private static final LocationTimeZoneAlgorithmStatus ARBITRARY_LOCATION_CERTAIN_STATUS =
+ new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
+ PROVIDER_STATUS_IS_CERTAIN, null, PROVIDER_STATUS_NOT_PRESENT, null);
+ private static final TimeZoneDetectorStatus ARBITRARY_DETECTOR_STATUS =
+ new TimeZoneDetectorStatus(DETECTOR_STATUS_RUNNING,
+ new TelephonyTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING),
+ ARBITRARY_LOCATION_CERTAIN_STATUS);
private static final int ARBITRARY_USER_ID = 9999;
private static final List<String> ARBITRARY_TIME_ZONE_IDS = Arrays.asList("TestZoneId");
private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234L;
@@ -113,7 +128,8 @@ public class TimeZoneDetectorServiceTest {
ConfigurationInternal configuration =
createConfigurationInternal(true /* autoDetectionEnabled*/);
- mFakeTimeZoneDetectorStrategySpy.initializeConfiguration(configuration);
+ mFakeTimeZoneDetectorStrategySpy.initializeConfigurationAndStatus(configuration,
+ ARBITRARY_DETECTOR_STATUS);
TimeZoneCapabilitiesAndConfig actualCapabilitiesAndConfig =
mTimeZoneDetectorService.getCapabilitiesAndConfig();
@@ -128,6 +144,7 @@ public class TimeZoneDetectorServiceTest {
TimeZoneCapabilitiesAndConfig expectedCapabilitiesAndConfig =
new TimeZoneCapabilitiesAndConfig(
+ ARBITRARY_DETECTOR_STATUS,
configuration.asCapabilities(expectedBypassUserPolicyChecks),
configuration.asConfiguration());
assertEquals(expectedCapabilitiesAndConfig, actualCapabilitiesAndConfig);
@@ -161,7 +178,9 @@ public class TimeZoneDetectorServiceTest {
public void testListenerRegistrationAndCallbacks() throws Exception {
ConfigurationInternal initialConfiguration =
createConfigurationInternal(false /* autoDetectionEnabled */);
- mFakeTimeZoneDetectorStrategySpy.initializeConfiguration(initialConfiguration);
+
+ mFakeTimeZoneDetectorStrategySpy.initializeConfigurationAndStatus(
+ initialConfiguration, ARBITRARY_DETECTOR_STATUS);
IBinder mockListenerBinder = mock(IBinder.class);
ITimeZoneDetectorListener mockListener = mock(ITimeZoneDetectorListener.class);
@@ -231,31 +250,35 @@ public class TimeZoneDetectorServiceTest {
}
@Test
- public void testSuggestGeolocationTimeZone_withoutPermission() {
+ public void testHandleLocationAlgorithmEvent_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion();
+ LocationAlgorithmEvent event = new LocationAlgorithmEvent(
+ ARBITRARY_LOCATION_CERTAIN_STATUS, timeZoneSuggestion);
assertThrows(SecurityException.class,
- () -> mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion));
+ () -> mTimeZoneDetectorService.handleLocationAlgorithmEvent(event));
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SET_TIME_ZONE), anyString());
}
@Test
- public void testSuggestGeolocationTimeZone() throws Exception {
+ public void testHandleLocationAlgorithmEvent() throws Exception {
doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion();
+ LocationAlgorithmEvent event = new LocationAlgorithmEvent(
+ ARBITRARY_LOCATION_CERTAIN_STATUS, timeZoneSuggestion);
- mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion);
+ mTimeZoneDetectorService.handleLocationAlgorithmEvent(event);
mTestHandler.assertTotalMessagesEnqueued(1);
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SET_TIME_ZONE), anyString());
mTestHandler.waitForMessagesToBeProcessed();
- verify(mFakeTimeZoneDetectorStrategySpy).suggestGeolocationTimeZone(timeZoneSuggestion);
+ verify(mFakeTimeZoneDetectorStrategySpy).handleLocationAlgorithmEvent(event);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index f50e7fbc76bb..b991c5a30415 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -16,6 +16,12 @@
package com.android.server.timezonedetector;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTOR_STATUS_RUNNING;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_CERTAIN;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_UNCERTAIN;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_PRESENT;
+import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_READY;
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET;
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY;
@@ -35,6 +41,7 @@ import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.T
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -47,8 +54,11 @@ import static org.mockito.Mockito.verify;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.app.time.LocationTimeZoneAlgorithmStatus;
+import android.app.time.TelephonyTimeZoneAlgorithmStatus;
import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
+import android.app.time.TimeZoneDetectorStatus;
import android.app.time.TimeZoneState;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
@@ -189,6 +199,9 @@ public class TimeZoneDetectorStrategyImplTest {
.setGeoDetectionEnabledSetting(true)
.build();
+ private static final TelephonyTimeZoneAlgorithmStatus TELEPHONY_ALGORITHM_RUNNING_STATUS =
+ new TelephonyTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING);
+
private FakeServiceConfigAccessor mFakeServiceConfigAccessorSpy;
private FakeEnvironment mFakeEnvironment;
private HandlerThread mHandlerThread;
@@ -233,9 +246,7 @@ public class TimeZoneDetectorStrategyImplTest {
{
mFakeServiceConfigAccessorSpy.simulateCurrentUserConfigurationInternalChange(
CONFIG_AUTO_DISABLED_GEO_DISABLED);
- mTestHandler.waitForMessagesToBeProcessed();
-
- stateChangeListener.assertNotificationsReceived(0);
+ assertStateChangeNotificationsSent(stateChangeListener, 0);
assertEquals(CONFIG_AUTO_DISABLED_GEO_DISABLED,
mTimeZoneDetectorStrategy.getCachedCapabilitiesAndConfigForTests());
}
@@ -244,10 +255,7 @@ public class TimeZoneDetectorStrategyImplTest {
{
mFakeServiceConfigAccessorSpy.simulateCurrentUserConfigurationInternalChange(
CONFIG_AUTO_ENABLED_GEO_ENABLED);
- mTestHandler.waitForMessagesToBeProcessed();
-
- stateChangeListener.assertNotificationsReceived(1);
- stateChangeListener.resetNotificationsReceivedCount();
+ assertStateChangeNotificationsSent(stateChangeListener, 1);
assertEquals(CONFIG_AUTO_ENABLED_GEO_ENABLED,
mTimeZoneDetectorStrategy.getCachedCapabilitiesAndConfigForTests());
}
@@ -258,10 +266,7 @@ public class TimeZoneDetectorStrategyImplTest {
new TimeZoneConfiguration.Builder().setGeoDetectionEnabled(false).build();
mTimeZoneDetectorStrategy.updateConfiguration(
USER_ID, requestedChanges, bypassUserPolicyChecks);
- mTestHandler.waitForMessagesToBeProcessed();
-
- stateChangeListener.assertNotificationsReceived(1);
- stateChangeListener.resetNotificationsReceivedCount();
+ assertStateChangeNotificationsSent(stateChangeListener, 1);
}
}
@@ -290,11 +295,9 @@ public class TimeZoneDetectorStrategyImplTest {
new TimeZoneConfiguration.Builder().setGeoDetectionEnabled(false).build();
mTimeZoneDetectorStrategy.updateConfiguration(
otherUserId, requestedChanges, bypassUserPolicyChecks);
- mTestHandler.waitForMessagesToBeProcessed();
// Only changes to the current user's config are notified.
- stateChangeListener.assertNotificationsReceived(0);
- stateChangeListener.resetNotificationsReceivedCount();
+ assertStateChangeNotificationsSent(stateChangeListener, 0);
}
// Current user behavior: the strategy caches and returns the latest configuration.
@@ -426,9 +429,9 @@ public class TimeZoneDetectorStrategyImplTest {
QualifiedTelephonyTimeZoneSuggestion expectedSlotIndex1ScoredSuggestion =
new QualifiedTelephonyTimeZoneSuggestion(slotIndex1TimeZoneSuggestion,
TELEPHONY_SCORE_NONE);
- assertEquals(expectedSlotIndex1ScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
- assertNull(mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2));
+ script.verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX1, expectedSlotIndex1ScoredSuggestion)
+ .verifyLatestQualifiedTelephonySuggestionReceived(SLOT_INDEX2, null);
assertEquals(expectedSlotIndex1ScoredSuggestion,
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
@@ -439,10 +442,10 @@ public class TimeZoneDetectorStrategyImplTest {
QualifiedTelephonyTimeZoneSuggestion expectedSlotIndex2ScoredSuggestion =
new QualifiedTelephonyTimeZoneSuggestion(slotIndex2TimeZoneSuggestion,
TELEPHONY_SCORE_NONE);
- assertEquals(expectedSlotIndex1ScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
- assertEquals(expectedSlotIndex2ScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2));
+ script.verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX1, expectedSlotIndex1ScoredSuggestion)
+ .verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX2, expectedSlotIndex2ScoredSuggestion);
// SlotIndex1 should always beat slotIndex2, all other things being equal.
assertEquals(expectedSlotIndex1ScoredSuggestion,
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
@@ -477,8 +480,8 @@ public class TimeZoneDetectorStrategyImplTest {
QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion =
new QualifiedTelephonyTimeZoneSuggestion(
lowQualitySuggestion, testCase.expectedScore);
- assertEquals(expectedScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+ script.verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX1, expectedScoredSuggestion);
assertEquals(expectedScoredSuggestion,
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
}
@@ -494,8 +497,8 @@ public class TimeZoneDetectorStrategyImplTest {
QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion =
new QualifiedTelephonyTimeZoneSuggestion(
goodQualitySuggestion, testCase2.expectedScore);
- assertEquals(expectedScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+ script.verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX1, expectedScoredSuggestion);
assertEquals(expectedScoredSuggestion,
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
}
@@ -511,8 +514,8 @@ public class TimeZoneDetectorStrategyImplTest {
QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion =
new QualifiedTelephonyTimeZoneSuggestion(
lowQualitySuggestion2, testCase.expectedScore);
- assertEquals(expectedScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+ script.verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX1, expectedScoredSuggestion);
assertEquals(expectedScoredSuggestion,
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
}
@@ -543,8 +546,8 @@ public class TimeZoneDetectorStrategyImplTest {
// Assert internal service state.
QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion =
new QualifiedTelephonyTimeZoneSuggestion(suggestion, testCase.expectedScore);
- assertEquals(expectedScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+ script.verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX1, expectedScoredSuggestion);
assertEquals(expectedScoredSuggestion,
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
@@ -560,8 +563,8 @@ public class TimeZoneDetectorStrategyImplTest {
}
// Assert internal service state.
- assertEquals(expectedScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+ script.verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX1, expectedScoredSuggestion);
assertEquals(expectedScoredSuggestion,
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
@@ -570,8 +573,8 @@ public class TimeZoneDetectorStrategyImplTest {
.verifyTimeZoneNotChanged();
// Assert internal service state.
- assertEquals(expectedScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+ script.verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX1, expectedScoredSuggestion);
assertEquals(expectedScoredSuggestion,
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
}
@@ -622,8 +625,8 @@ public class TimeZoneDetectorStrategyImplTest {
}
// Assert internal service state.
- assertEquals(expectedZoneSlotIndex1ScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+ script.verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX1, expectedZoneSlotIndex1ScoredSuggestion);
assertEquals(expectedZoneSlotIndex1ScoredSuggestion,
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
}
@@ -677,10 +680,10 @@ public class TimeZoneDetectorStrategyImplTest {
}
// Assert internal service state.
- assertEquals(expectedZoneSlotIndex1ScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
- assertEquals(expectedEmptySlotIndex2ScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2));
+ script.verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX1, expectedZoneSlotIndex1ScoredSuggestion)
+ .verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX2, expectedEmptySlotIndex2ScoredSuggestion);
assertEquals(expectedZoneSlotIndex1ScoredSuggestion,
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
@@ -690,10 +693,10 @@ public class TimeZoneDetectorStrategyImplTest {
script.verifyTimeZoneNotChanged();
// Assert internal service state.
- assertEquals(expectedZoneSlotIndex1ScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
- assertEquals(expectedZoneSlotIndex2ScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2));
+ script.verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX1, expectedZoneSlotIndex1ScoredSuggestion)
+ .verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX2, expectedZoneSlotIndex2ScoredSuggestion);
// SlotIndex1 should always beat slotIndex2, all other things being equal.
assertEquals(expectedZoneSlotIndex1ScoredSuggestion,
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
@@ -709,20 +712,20 @@ public class TimeZoneDetectorStrategyImplTest {
}
// Assert internal service state.
- assertEquals(expectedEmptySlotIndex1ScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
- assertEquals(expectedZoneSlotIndex2ScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2));
+ script.verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX1, expectedEmptySlotIndex1ScoredSuggestion)
+ .verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX2, expectedZoneSlotIndex2ScoredSuggestion);
assertEquals(expectedZoneSlotIndex2ScoredSuggestion,
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
// Reset the state for the next loop.
script.simulateTelephonyTimeZoneSuggestion(emptySlotIndex2Suggestion)
.verifyTimeZoneNotChanged();
- assertEquals(expectedEmptySlotIndex1ScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
- assertEquals(expectedEmptySlotIndex2ScoredSuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2));
+ script.verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX1, expectedEmptySlotIndex1ScoredSuggestion)
+ .verifyLatestQualifiedTelephonySuggestionReceived(
+ SLOT_INDEX2, expectedEmptySlotIndex2ScoredSuggestion);
}
}
@@ -866,53 +869,185 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Test
- public void testGeoSuggestion_uncertain() {
+ public void testLocationAlgorithmEvent_statusChangesOnly() {
+ TestStateChangeListener stateChangeListener = new TestStateChangeListener();
Script script = new Script()
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED_GEO_ENABLED)
- .resetConfigurationTracking();
+ .resetConfigurationTracking()
+ .registerStateChangeListener(stateChangeListener);
+
+ TimeZoneDetectorStatus expectedInitialDetectorStatus = new TimeZoneDetectorStatus(
+ DETECTOR_STATUS_RUNNING,
+ TELEPHONY_ALGORITHM_RUNNING_STATUS,
+ LocationTimeZoneAlgorithmStatus.UNKNOWN);
+ script.verifyCachedDetectorStatus(expectedInitialDetectorStatus);
+
+ LocationTimeZoneAlgorithmStatus algorithmStatus1 = new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING, PROVIDER_STATUS_NOT_READY, null,
+ PROVIDER_STATUS_NOT_PRESENT, null);
+ LocationTimeZoneAlgorithmStatus algorithmStatus2 = new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING, PROVIDER_STATUS_NOT_PRESENT, null,
+ PROVIDER_STATUS_NOT_PRESENT, null);
+ assertNotEquals(algorithmStatus1, algorithmStatus2);
+
+ {
+ LocationAlgorithmEvent locationAlgorithmEvent =
+ new LocationAlgorithmEvent(algorithmStatus1, null);
+ script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
+ .verifyTimeZoneNotChanged();
- GeolocationTimeZoneSuggestion uncertainSuggestion = createUncertainGeolocationSuggestion();
+ assertStateChangeNotificationsSent(stateChangeListener, 1);
- script.simulateGeolocationTimeZoneSuggestion(uncertainSuggestion)
+ // Assert internal service state.
+ TimeZoneDetectorStatus expectedDetectorStatus = new TimeZoneDetectorStatus(
+ DETECTOR_STATUS_RUNNING,
+ TELEPHONY_ALGORITHM_RUNNING_STATUS,
+ algorithmStatus1);
+ script.verifyCachedDetectorStatus(expectedDetectorStatus)
+ .verifyLatestLocationAlgorithmEventReceived(locationAlgorithmEvent);
+
+ // Repeat the event to demonstrate the state change notifier is not triggered.
+ script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
+ .verifyTimeZoneNotChanged();
+
+ assertStateChangeNotificationsSent(stateChangeListener, 0);
+
+ // Assert internal service state.
+ script.verifyCachedDetectorStatus(expectedDetectorStatus)
+ .verifyLatestLocationAlgorithmEventReceived(locationAlgorithmEvent);
+ }
+
+ {
+ LocationAlgorithmEvent locationAlgorithmEvent =
+ new LocationAlgorithmEvent(algorithmStatus2, null);
+ script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
+ .verifyTimeZoneNotChanged();
+
+ assertStateChangeNotificationsSent(stateChangeListener, 1);
+
+ // Assert internal service state.
+ TimeZoneDetectorStatus expectedDetectorStatus = new TimeZoneDetectorStatus(
+ DETECTOR_STATUS_RUNNING,
+ TELEPHONY_ALGORITHM_RUNNING_STATUS,
+ algorithmStatus2);
+ script.verifyCachedDetectorStatus(expectedDetectorStatus)
+ .verifyLatestLocationAlgorithmEventReceived(locationAlgorithmEvent);
+
+ // Repeat the event to demonstrate the state change notifier is not triggered.
+ script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
+ .verifyTimeZoneNotChanged();
+
+ assertStateChangeNotificationsSent(stateChangeListener, 0);
+
+ // Assert internal service state.
+ script.verifyCachedDetectorStatus(expectedDetectorStatus)
+ .verifyLatestLocationAlgorithmEventReceived(locationAlgorithmEvent);
+ }
+ }
+
+ @Test
+ public void testLocationAlgorithmEvent_uncertain() {
+ TestStateChangeListener stateChangeListener = new TestStateChangeListener();
+ Script script = new Script()
+ .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
+ .simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED_GEO_ENABLED)
+ .resetConfigurationTracking()
+ .registerStateChangeListener(stateChangeListener);
+
+ LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent();
+ script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged();
+ assertStateChangeNotificationsSent(stateChangeListener, 1);
+
// Assert internal service state.
- assertEquals(uncertainSuggestion,
- mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+ TimeZoneDetectorStatus expectedDetectorStatus = new TimeZoneDetectorStatus(
+ DETECTOR_STATUS_RUNNING,
+ TELEPHONY_ALGORITHM_RUNNING_STATUS,
+ locationAlgorithmEvent.getAlgorithmStatus());
+ script.verifyCachedDetectorStatus(expectedDetectorStatus)
+ .verifyLatestLocationAlgorithmEventReceived(locationAlgorithmEvent);
+
+ // Repeat the event to demonstrate the state change notifier is not triggered.
+ script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
+ .verifyTimeZoneNotChanged();
+
+ // Detector remains running and location algorithm is still uncertain so nothing to report.
+ assertStateChangeNotificationsSent(stateChangeListener, 0);
+
+ // Assert internal service state.
+ script.verifyCachedDetectorStatus(expectedDetectorStatus)
+ .verifyLatestLocationAlgorithmEventReceived(locationAlgorithmEvent);
}
@Test
- public void testGeoSuggestion_noZones() {
+ public void testLocationAlgorithmEvent_noZones() {
+ TestStateChangeListener stateChangeListener = new TestStateChangeListener();
Script script = new Script()
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED_GEO_ENABLED)
- .resetConfigurationTracking();
+ .resetConfigurationTracking()
+ .registerStateChangeListener(stateChangeListener);
+
+ LocationAlgorithmEvent locationAlgorithmEvent = createCertainLocationAlgorithmEvent();
+ script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
+ .verifyTimeZoneNotChanged();
- GeolocationTimeZoneSuggestion noZonesSuggestion = createCertainGeolocationSuggestion();
+ assertStateChangeNotificationsSent(stateChangeListener, 1);
- script.simulateGeolocationTimeZoneSuggestion(noZonesSuggestion)
+ // Assert internal service state.
+ TimeZoneDetectorStatus expectedDetectorStatus = new TimeZoneDetectorStatus(
+ DETECTOR_STATUS_RUNNING,
+ TELEPHONY_ALGORITHM_RUNNING_STATUS,
+ locationAlgorithmEvent.getAlgorithmStatus());
+ script.verifyCachedDetectorStatus(expectedDetectorStatus)
+ .verifyLatestLocationAlgorithmEventReceived(locationAlgorithmEvent);
+
+ // Repeat the event to demonstrate the state change notifier is not triggered.
+ script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged();
+ assertStateChangeNotificationsSent(stateChangeListener, 0);
+
// Assert internal service state.
- assertEquals(noZonesSuggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+ script.verifyCachedDetectorStatus(expectedDetectorStatus)
+ .verifyLatestLocationAlgorithmEventReceived(locationAlgorithmEvent);
}
@Test
- public void testGeoSuggestion_oneZone() {
- GeolocationTimeZoneSuggestion suggestion =
- createCertainGeolocationSuggestion("Europe/London");
-
+ public void testLocationAlgorithmEvent_oneZone() {
+ TestStateChangeListener stateChangeListener = new TestStateChangeListener();
Script script = new Script()
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED_GEO_ENABLED)
- .resetConfigurationTracking();
+ .resetConfigurationTracking()
+ .registerStateChangeListener(stateChangeListener);
+
+ LocationAlgorithmEvent locationAlgorithmEvent =
+ createCertainLocationAlgorithmEvent("Europe/London");
+ script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
+ .verifyTimeZoneChangedAndReset(locationAlgorithmEvent);
+
+ assertStateChangeNotificationsSent(stateChangeListener, 1);
+
+ // Assert internal service state.
+ TimeZoneDetectorStatus expectedDetectorStatus = new TimeZoneDetectorStatus(
+ DETECTOR_STATUS_RUNNING,
+ TELEPHONY_ALGORITHM_RUNNING_STATUS,
+ locationAlgorithmEvent.getAlgorithmStatus());
+ script.verifyCachedDetectorStatus(expectedDetectorStatus)
+ .verifyLatestLocationAlgorithmEventReceived(locationAlgorithmEvent);
+
+ // Repeat the event to demonstrate the state change notifier is not triggered.
+ script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
+ .verifyTimeZoneNotChanged();
- script.simulateGeolocationTimeZoneSuggestion(suggestion)
- .verifyTimeZoneChangedAndReset(suggestion);
+ assertStateChangeNotificationsSent(stateChangeListener, 0);
// Assert internal service state.
- assertEquals(suggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+ script.verifyCachedDetectorStatus(expectedDetectorStatus)
+ .verifyLatestLocationAlgorithmEventReceived(locationAlgorithmEvent);
}
/**
@@ -921,41 +1056,35 @@ public class TimeZoneDetectorStrategyImplTest {
* set to until that unambiguously can't be correct.
*/
@Test
- public void testGeoSuggestion_multiZone() {
- GeolocationTimeZoneSuggestion londonOnlySuggestion =
- createCertainGeolocationSuggestion("Europe/London");
- GeolocationTimeZoneSuggestion londonOrParisSuggestion =
- createCertainGeolocationSuggestion("Europe/Paris", "Europe/London");
- GeolocationTimeZoneSuggestion parisOnlySuggestion =
- createCertainGeolocationSuggestion("Europe/Paris");
-
+ public void testLocationAlgorithmEvent_multiZone() {
Script script = new Script()
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED_GEO_ENABLED)
.resetConfigurationTracking();
- script.simulateGeolocationTimeZoneSuggestion(londonOnlySuggestion)
- .verifyTimeZoneChangedAndReset(londonOnlySuggestion);
- assertEquals(londonOnlySuggestion,
- mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+ LocationAlgorithmEvent londonOnlyEvent =
+ createCertainLocationAlgorithmEvent("Europe/London");
+ script.simulateLocationAlgorithmEvent(londonOnlyEvent)
+ .verifyTimeZoneChangedAndReset(londonOnlyEvent)
+ .verifyLatestLocationAlgorithmEventReceived(londonOnlyEvent);
// Confirm bias towards the current device zone when there's multiple zones to choose from.
- script.simulateGeolocationTimeZoneSuggestion(londonOrParisSuggestion)
- .verifyTimeZoneNotChanged();
- assertEquals(londonOrParisSuggestion,
- mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+ LocationAlgorithmEvent londonOrParisEvent =
+ createCertainLocationAlgorithmEvent("Europe/Paris", "Europe/London");
+ script.simulateLocationAlgorithmEvent(londonOrParisEvent)
+ .verifyTimeZoneNotChanged()
+ .verifyLatestLocationAlgorithmEventReceived(londonOrParisEvent);
- script.simulateGeolocationTimeZoneSuggestion(parisOnlySuggestion)
- .verifyTimeZoneChangedAndReset(parisOnlySuggestion);
- assertEquals(parisOnlySuggestion,
- mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+ LocationAlgorithmEvent parisOnlyEvent = createCertainLocationAlgorithmEvent("Europe/Paris");
+ script.simulateLocationAlgorithmEvent(parisOnlyEvent)
+ .verifyTimeZoneChangedAndReset(parisOnlyEvent)
+ .verifyLatestLocationAlgorithmEventReceived(parisOnlyEvent);
// Now the suggestion that previously left the device on Europe/London will leave the device
// on Europe/Paris.
- script.simulateGeolocationTimeZoneSuggestion(londonOrParisSuggestion)
- .verifyTimeZoneNotChanged();
- assertEquals(londonOrParisSuggestion,
- mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+ script.simulateLocationAlgorithmEvent(londonOrParisEvent)
+ .verifyTimeZoneNotChanged()
+ .verifyLatestLocationAlgorithmEventReceived(londonOrParisEvent);
}
/**
@@ -964,8 +1093,9 @@ public class TimeZoneDetectorStrategyImplTest {
*/
@Test
public void testChangingGeoDetectionEnabled() {
- GeolocationTimeZoneSuggestion geolocationSuggestion =
- createCertainGeolocationSuggestion("Europe/London");
+ TestStateChangeListener stateChangeListener = new TestStateChangeListener();
+ LocationAlgorithmEvent locationAlgorithmEvent =
+ createCertainLocationAlgorithmEvent("Europe/London");
TelephonyTimeZoneSuggestion telephonySuggestion = createTelephonySuggestion(
SLOT_INDEX1, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
"Europe/Paris");
@@ -973,20 +1103,22 @@ public class TimeZoneDetectorStrategyImplTest {
Script script = new Script()
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED_GEO_DISABLED)
- .resetConfigurationTracking();
+ .resetConfigurationTracking()
+ .registerStateChangeListener(stateChangeListener);
// Add suggestions. Nothing should happen as time zone detection is disabled.
- script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
- .verifyTimeZoneNotChanged();
+ script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
+ .verifyTimeZoneNotChanged()
+ .verifyLatestLocationAlgorithmEventReceived(locationAlgorithmEvent);
- assertEquals(geolocationSuggestion,
- mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+ // A detector status change is considered a "state change".
+ assertStateChangeNotificationsSent(stateChangeListener, 1);
script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
- .verifyTimeZoneNotChanged();
+ .verifyTimeZoneNotChanged()
+ .verifyLatestTelephonySuggestionReceived(SLOT_INDEX1, telephonySuggestion);
- assertEquals(telephonySuggestion,
- mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1).suggestion);
+ assertStateChangeNotificationsSent(stateChangeListener, 0);
// Toggling the time zone detection enabled setting on should cause the device setting to be
// set from the telephony signal, as we've started with geolocation time zone detection
@@ -994,18 +1126,25 @@ public class TimeZoneDetectorStrategyImplTest {
script.simulateSetAutoMode(true)
.verifyTimeZoneChangedAndReset(telephonySuggestion);
+ // A configuration change is considered a "state change".
+ assertStateChangeNotificationsSent(stateChangeListener, 1);
+
// Changing the detection to enable geo detection will cause the device tz setting to
// change to use the latest geolocation suggestion.
script.simulateSetGeoDetectionEnabled(true)
- .verifyTimeZoneChangedAndReset(geolocationSuggestion);
+ .verifyTimeZoneChangedAndReset(locationAlgorithmEvent);
+
+ // A configuration change is considered a "state change".
+ assertStateChangeNotificationsSent(stateChangeListener, 1);
// Changing the detection to disable geo detection should cause the device tz setting to
// change to the telephony suggestion.
script.simulateSetGeoDetectionEnabled(false)
- .verifyTimeZoneChangedAndReset(telephonySuggestion);
+ .verifyTimeZoneChangedAndReset(telephonySuggestion)
+ .verifyLatestLocationAlgorithmEventReceived(locationAlgorithmEvent);
- assertEquals(geolocationSuggestion,
- mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+ // A configuration change is considered a "state change".
+ assertStateChangeNotificationsSent(stateChangeListener, 1);
}
@Test
@@ -1039,21 +1178,20 @@ public class TimeZoneDetectorStrategyImplTest {
// Receiving an "uncertain" geolocation suggestion should have no effect.
{
- GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
- createUncertainGeolocationSuggestion();
+ LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
- .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(true);
}
// Receiving a "certain" geolocation suggestion should disable telephony fallback mode.
{
- GeolocationTimeZoneSuggestion geolocationSuggestion =
- createCertainGeolocationSuggestion("Europe/London");
+ LocationAlgorithmEvent locationAlgorithmEvent =
+ createCertainLocationAlgorithmEvent("Europe/London");
script.simulateIncrementClock()
- .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
- .verifyTimeZoneChangedAndReset(geolocationSuggestion)
+ .simulateLocationAlgorithmEvent(locationAlgorithmEvent)
+ .verifyTimeZoneChangedAndReset(locationAlgorithmEvent)
.verifyTelephonyFallbackIsEnabled(false);
}
@@ -1076,22 +1214,22 @@ public class TimeZoneDetectorStrategyImplTest {
// Geolocation suggestions should continue to be used as normal (previous telephony
// suggestions are not used, even when the geolocation suggestion is uncertain).
{
- GeolocationTimeZoneSuggestion geolocationSuggestion =
- createCertainGeolocationSuggestion("Europe/Rome");
+ LocationAlgorithmEvent certainLocationAlgorithmEvent =
+ createCertainLocationAlgorithmEvent("Europe/Rome");
script.simulateIncrementClock()
- .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
- .verifyTimeZoneChangedAndReset(geolocationSuggestion)
+ .simulateLocationAlgorithmEvent(certainLocationAlgorithmEvent)
+ .verifyTimeZoneChangedAndReset(certainLocationAlgorithmEvent)
.verifyTelephonyFallbackIsEnabled(false);
- GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
- createUncertainGeolocationSuggestion();
+ LocationAlgorithmEvent uncertainLocationAlgorithmEvent =
+ createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
- .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .simulateLocationAlgorithmEvent(uncertainLocationAlgorithmEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false);
script.simulateIncrementClock()
- .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
+ .simulateLocationAlgorithmEvent(certainLocationAlgorithmEvent)
// No change needed, device will already be set to Europe/Rome.
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false);
@@ -1108,21 +1246,20 @@ public class TimeZoneDetectorStrategyImplTest {
// Make the geolocation algorithm uncertain.
{
- GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
- createUncertainGeolocationSuggestion();
+ LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
- .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneChangedAndReset(lastTelephonySuggestion)
.verifyTelephonyFallbackIsEnabled(true);
}
// Make the geolocation algorithm certain, disabling telephony fallback.
{
- GeolocationTimeZoneSuggestion geolocationSuggestion =
- createCertainGeolocationSuggestion("Europe/Lisbon");
+ LocationAlgorithmEvent locationAlgorithmEvent =
+ createCertainLocationAlgorithmEvent("Europe/Lisbon");
script.simulateIncrementClock()
- .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
- .verifyTimeZoneChangedAndReset(geolocationSuggestion)
+ .simulateLocationAlgorithmEvent(locationAlgorithmEvent)
+ .verifyTimeZoneChangedAndReset(locationAlgorithmEvent)
.verifyTelephonyFallbackIsEnabled(false);
}
@@ -1130,10 +1267,9 @@ public class TimeZoneDetectorStrategyImplTest {
// Demonstrate what happens when geolocation is uncertain when telephony fallback is
// enabled.
{
- GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
- createUncertainGeolocationSuggestion();
+ LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
- .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false)
.simulateEnableTelephonyFallback()
@@ -1161,10 +1297,9 @@ public class TimeZoneDetectorStrategyImplTest {
// Receiving an "uncertain" geolocation suggestion should have no effect.
{
- GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
- createUncertainGeolocationSuggestion();
+ LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
- .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(true);
}
@@ -1172,10 +1307,9 @@ public class TimeZoneDetectorStrategyImplTest {
// Make an uncertain geolocation suggestion, there is no telephony suggestion to fall back
// to
{
- GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
- createUncertainGeolocationSuggestion();
+ LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
- .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(true);
}
@@ -1185,17 +1319,16 @@ public class TimeZoneDetectorStrategyImplTest {
// Geolocation suggestions should continue to be used as normal (previous telephony
// suggestions are not used, even when the geolocation suggestion is uncertain).
{
- GeolocationTimeZoneSuggestion geolocationSuggestion =
- createCertainGeolocationSuggestion("Europe/Rome");
+ LocationAlgorithmEvent certainEvent =
+ createCertainLocationAlgorithmEvent("Europe/Rome");
script.simulateIncrementClock()
- .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
- .verifyTimeZoneChangedAndReset(geolocationSuggestion)
+ .simulateLocationAlgorithmEvent(certainEvent)
+ .verifyTimeZoneChangedAndReset(certainEvent)
.verifyTelephonyFallbackIsEnabled(false);
- GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
- createUncertainGeolocationSuggestion();
+ LocationAlgorithmEvent uncertainEvent = createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
- .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .simulateLocationAlgorithmEvent(uncertainEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false);
@@ -1319,15 +1452,15 @@ public class TimeZoneDetectorStrategyImplTest {
TelephonyTimeZoneSuggestion telephonySuggestion =
createTelephonySuggestion(0 /* slotIndex */, MATCH_TYPE_NETWORK_COUNTRY_ONLY,
QUALITY_SINGLE_ZONE, "Zone2");
- GeolocationTimeZoneSuggestion geolocationTimeZoneSuggestion =
- createCertainGeolocationSuggestion("Zone3", "Zone2");
+ LocationAlgorithmEvent locationAlgorithmEvent =
+ createCertainLocationAlgorithmEvent("Zone3", "Zone2");
script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
.verifyTimeZoneNotChanged()
- .simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion)
+ .simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged();
assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
- manualSuggestion, telephonySuggestion, geolocationTimeZoneSuggestion,
+ manualSuggestion, telephonySuggestion, locationAlgorithmEvent,
MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
// Update the config and confirm that the config metrics state updates also.
@@ -1336,11 +1469,11 @@ public class TimeZoneDetectorStrategyImplTest {
.setGeoDetectionEnabledSetting(true)
.build();
- expectedDeviceTimeZoneId = geolocationTimeZoneSuggestion.getZoneIds().get(0);
+ expectedDeviceTimeZoneId = locationAlgorithmEvent.getSuggestion().getZoneIds().get(0);
script.simulateConfigurationInternalChange(expectedInternalConfig)
.verifyTimeZoneChangedAndReset(expectedDeviceTimeZoneId, TIME_ZONE_CONFIDENCE_HIGH);
assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
- manualSuggestion, telephonySuggestion, geolocationTimeZoneSuggestion,
+ manualSuggestion, telephonySuggestion, locationAlgorithmEvent,
MetricsTimeZoneDetectorState.DETECTION_MODE_GEO);
}
@@ -1352,7 +1485,7 @@ public class TimeZoneDetectorStrategyImplTest {
ConfigurationInternal expectedInternalConfig,
String expectedDeviceTimeZoneId, ManualTimeZoneSuggestion expectedManualSuggestion,
TelephonyTimeZoneSuggestion expectedTelephonySuggestion,
- GeolocationTimeZoneSuggestion expectedGeolocationTimeZoneSuggestion,
+ LocationAlgorithmEvent expectedLocationAlgorithmEvent,
int expectedDetectionMode) {
MetricsTimeZoneDetectorState actualState = mTimeZoneDetectorStrategy.generateMetricsState();
@@ -1365,7 +1498,7 @@ public class TimeZoneDetectorStrategyImplTest {
MetricsTimeZoneDetectorState.create(
tzIdOrdinalGenerator, expectedInternalConfig, expectedDeviceTimeZoneId,
expectedManualSuggestion, expectedTelephonySuggestion,
- expectedGeolocationTimeZoneSuggestion);
+ expectedLocationAlgorithmEvent);
// Rely on MetricsTimeZoneDetectorState.equals() for time zone ID / ID ordinal comparisons.
assertEquals(expectedState, actualState);
}
@@ -1405,20 +1538,37 @@ public class TimeZoneDetectorStrategyImplTest {
return new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX2).build();
}
+ private LocationAlgorithmEvent createCertainLocationAlgorithmEvent(@NonNull String... zoneIds) {
+ GeolocationTimeZoneSuggestion suggestion = createCertainGeolocationSuggestion(zoneIds);
+ LocationTimeZoneAlgorithmStatus algorithmStatus = new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING, PROVIDER_STATUS_IS_CERTAIN, null,
+ PROVIDER_STATUS_NOT_PRESENT, null);
+ LocationAlgorithmEvent event = new LocationAlgorithmEvent(algorithmStatus, suggestion);
+ event.addDebugInfo("Test certain event");
+ return event;
+ }
+
+ private LocationAlgorithmEvent createUncertainLocationAlgorithmEvent() {
+ GeolocationTimeZoneSuggestion suggestion = createUncertainGeolocationSuggestion();
+ LocationTimeZoneAlgorithmStatus algorithmStatus = new LocationTimeZoneAlgorithmStatus(
+ DETECTION_ALGORITHM_STATUS_RUNNING, PROVIDER_STATUS_IS_UNCERTAIN, null,
+ PROVIDER_STATUS_NOT_PRESENT, null);
+ LocationAlgorithmEvent event = new LocationAlgorithmEvent(algorithmStatus, suggestion);
+ event.addDebugInfo("Test uncertain event");
+ return event;
+ }
+
private GeolocationTimeZoneSuggestion createUncertainGeolocationSuggestion() {
- return GeolocationTimeZoneSuggestion.createCertainSuggestion(
- mFakeEnvironment.elapsedRealtimeMillis(), null);
+ return GeolocationTimeZoneSuggestion.createUncertainSuggestion(
+ mFakeEnvironment.elapsedRealtimeMillis());
}
private GeolocationTimeZoneSuggestion createCertainGeolocationSuggestion(
@NonNull String... zoneIds) {
assertNotNull(zoneIds);
- GeolocationTimeZoneSuggestion suggestion =
- GeolocationTimeZoneSuggestion.createCertainSuggestion(
- mFakeEnvironment.elapsedRealtimeMillis(), Arrays.asList(zoneIds));
- suggestion.addDebugInfo("Test suggestion");
- return suggestion;
+ return GeolocationTimeZoneSuggestion.createCertainSuggestion(
+ mFakeEnvironment.elapsedRealtimeMillis(), Arrays.asList(zoneIds));
}
static class FakeEnvironment implements TimeZoneDetectorStrategyImpl.Environment {
@@ -1499,6 +1649,14 @@ public class TimeZoneDetectorStrategyImplTest {
}
}
+ private void assertStateChangeNotificationsSent(
+ TestStateChangeListener stateChangeListener, int expectedCount) {
+ // State change notifications are asynchronous, so we have to wait.
+ mTestHandler.waitForMessagesToBeProcessed();
+
+ stateChangeListener.assertNotificationsReceivedAndReset(expectedCount);
+ }
+
/**
* A "fluent" class allows reuse of code in tests: initialization, simulation and verification
* logic.
@@ -1516,6 +1674,11 @@ public class TimeZoneDetectorStrategyImplTest {
return this;
}
+ Script registerStateChangeListener(StateChangeListener stateChangeListener) {
+ mTimeZoneDetectorStrategy.addChangeListener(stateChangeListener);
+ return this;
+ }
+
Script simulateIncrementClock() {
mFakeEnvironment.incrementClock();
return this;
@@ -1555,11 +1718,10 @@ public class TimeZoneDetectorStrategyImplTest {
}
/**
- * Simulates the time zone detection strategy receiving a geolocation-originated
- * suggestion.
+ * Simulates the time zone detection strategy receiving a location algorithm event.
*/
- Script simulateGeolocationTimeZoneSuggestion(GeolocationTimeZoneSuggestion suggestion) {
- mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(suggestion);
+ Script simulateLocationAlgorithmEvent(LocationAlgorithmEvent event) {
+ mTimeZoneDetectorStrategy.handleLocationAlgorithmEvent(event);
return this;
}
@@ -1616,7 +1778,9 @@ public class TimeZoneDetectorStrategyImplTest {
return this;
}
- Script verifyTimeZoneChangedAndReset(GeolocationTimeZoneSuggestion suggestion) {
+ Script verifyTimeZoneChangedAndReset(LocationAlgorithmEvent event) {
+ GeolocationTimeZoneSuggestion suggestion = event.getSuggestion();
+ assertNotNull("Only events with suggestions can change the time zone", suggestion);
assertEquals("Only use this method with unambiguous geo suggestions",
1, suggestion.getZoneIds().size());
verifyTimeZoneChangedAndReset(
@@ -1631,6 +1795,32 @@ public class TimeZoneDetectorStrategyImplTest {
return this;
}
+ Script verifyCachedDetectorStatus(TimeZoneDetectorStatus expectedStatus) {
+ assertEquals(expectedStatus,
+ mTimeZoneDetectorStrategy.getCachedDetectorStatusForTests());
+ return this;
+ }
+
+ Script verifyLatestLocationAlgorithmEventReceived(LocationAlgorithmEvent expectedEvent) {
+ assertEquals(expectedEvent,
+ mTimeZoneDetectorStrategy.getLatestLocationAlgorithmEvent());
+ return this;
+ }
+
+ Script verifyLatestTelephonySuggestionReceived(int slotIndex,
+ TelephonyTimeZoneSuggestion expectedSuggestion) {
+ assertEquals(expectedSuggestion,
+ mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(slotIndex).suggestion);
+ return this;
+ }
+
+ Script verifyLatestQualifiedTelephonySuggestionReceived(int slotIndex,
+ QualifiedTelephonyTimeZoneSuggestion expectedQualifiedSuggestion) {
+ assertEquals(expectedQualifiedSuggestion,
+ mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(slotIndex));
+ return this;
+ }
+
Script resetConfigurationTracking() {
mFakeEnvironment.commitAllChanges();
return this;
@@ -1671,11 +1861,16 @@ public class TimeZoneDetectorStrategyImplTest {
mNotificationsReceived++;
}
- public void resetNotificationsReceivedCount() {
+ public void assertNotificationsReceivedAndReset(int expectedCount) {
+ assertNotificationsReceived(expectedCount);
+ resetNotificationsReceivedCount();
+ }
+
+ private void resetNotificationsReceivedCount() {
mNotificationsReceived = 0;
}
- public void assertNotificationsReceived(int expectedCount) {
+ private void assertNotificationsReceived(int expectedCount) {
assertEquals(expectedCount, mNotificationsReceived);
}
}
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 c18acd20e96a..b08705be2eac 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,8 @@
*/
package com.android.server.timezonedetector.location;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_RUNNING;
+import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_NOT_APPLICABLE;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE;
@@ -42,6 +44,7 @@ import static com.android.server.timezonedetector.location.TestSupport.USER2_CON
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -51,6 +54,7 @@ import static java.util.Arrays.asList;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.time.DetectorStatusTypes.DetectionAlgorithmStatus;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.service.timezone.TimeZoneProviderEvent;
@@ -60,6 +64,7 @@ import android.util.IndentingPrintWriter;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.LocationAlgorithmEvent;
import com.android.server.timezonedetector.TestState;
import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger;
import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
@@ -141,7 +146,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestPrimaryLocationTimeZoneProvider.setFailDuringInitialization(true);
// Initialize. After initialization the providers must be initialized and one should be
- // started.
+ // started. They should report their status change via the callback.
controller.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertInitialized();
@@ -154,7 +159,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout);
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -184,7 +190,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -211,7 +218,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING, STATE_FAILED);
- mTestCallback.assertUncertainSuggestionMadeAndCommit();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -239,7 +247,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -262,7 +271,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_PROVIDERS_INITIALIZING, STATE_STOPPED);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -282,7 +292,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate time passing with no provider event being received from the primary.
@@ -296,7 +307,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate time passing with no provider event being received from either the primary or
@@ -311,7 +322,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
// Finally, the uncertainty timeout should cause the controller to make an uncertain
@@ -324,7 +335,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit(STATE_UNCERTAIN);
- mTestCallback.assertUncertainSuggestionMadeAndCommit();
+ mTestCallback.assertEventWithUncertainSuggestionReportedAndCommit();
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -345,7 +356,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a location event being received from the primary provider. This should cause a
@@ -358,7 +370,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -380,7 +392,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate time passing with no provider event being received from the primary.
@@ -392,7 +405,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate a location event being received from the primary provider. This should cause a
@@ -405,7 +418,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -427,7 +440,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate time passing with no provider event being received from the primary.
@@ -439,7 +453,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate a location event being received from the secondary provider. This should cause a
@@ -453,7 +467,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -475,7 +489,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a location event being received from the primary provider. This should cause a
@@ -488,7 +503,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
assertFalse(controller.isUncertaintyTimeoutSet());
@@ -501,7 +516,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertFalse(controller.isUncertaintyTimeoutSet());
// And a third, different event should cause another suggestion.
@@ -513,7 +528,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -535,7 +550,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate time passing with no provider event being received from the primary.
@@ -547,7 +563,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate a location event being received from the secondary provider. This should cause a
@@ -561,7 +577,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
assertFalse(controller.isUncertaintyTimeoutSet());
@@ -575,7 +591,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertFalse(controller.isUncertaintyTimeoutSet());
// And a third, different event should cause another suggestion.
@@ -588,7 +604,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -610,7 +626,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a location event being received from the primary provider. This should cause a
@@ -623,7 +640,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
assertFalse(controller.isUncertaintyTimeoutSet());
@@ -639,7 +656,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate a location event being received from the secondary provider. This should cause a
@@ -654,7 +671,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
assertFalse(controller.isUncertaintyTimeoutSet());
@@ -670,7 +687,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate time passing. This means the uncertainty timeout should fire and the uncertain
@@ -683,7 +700,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit(STATE_UNCERTAIN);
- mTestCallback.assertUncertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithUncertainSuggestionReportedAndCommit(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -705,7 +722,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a location event being received from the primary provider. This should cause a
@@ -718,7 +736,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
assertFalse(controller.isUncertaintyTimeoutSet());
@@ -733,7 +751,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
// And a success event from the primary provider should cause the controller to make another
@@ -747,7 +765,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -767,7 +785,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_PROVIDERS_INITIALIZING, STATE_STOPPED);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is enabled.
@@ -778,7 +797,8 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is disabled.
@@ -788,7 +808,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_STOPPED);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -807,7 +828,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_PROVIDERS_INITIALIZING, STATE_STOPPED);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is enabled.
@@ -818,7 +840,8 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a success event being received from the primary provider.
@@ -830,7 +853,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
assertFalse(controller.isUncertaintyTimeoutSet());
@@ -843,8 +866,9 @@ public class LocationTimeZoneProviderControllerTest {
assertControllerState(controller, STATE_STOPPED);
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
- mTestMetricsLogger.assertStateChangesAndCommit(STATE_UNCERTAIN, STATE_STOPPED);
- mTestCallback.assertUncertainSuggestionMadeAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_STOPPED);
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -865,7 +889,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate the primary provider suggesting a time zone.
@@ -879,7 +904,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
assertFalse(controller.isUncertaintyTimeoutSet());
@@ -897,9 +922,9 @@ public class LocationTimeZoneProviderControllerTest {
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfig(
PROVIDER_STATE_STARTED_INITIALIZING, USER2_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
- mTestMetricsLogger.assertStateChangesAndCommit(
- STATE_UNCERTAIN, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertUncertainSuggestionMadeAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_STOPPED, STATE_INITIALIZING);
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -920,7 +945,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a failure location event being received from the primary provider. This should
@@ -933,7 +959,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate uncertainty from the secondary.
@@ -945,7 +971,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
// And a success event from the secondary provider should cause the controller to make
@@ -958,7 +984,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
assertFalse(controller.isUncertaintyTimeoutSet());
@@ -971,7 +997,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
}
@@ -992,7 +1018,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a failure location event being received from the primary provider. This should
@@ -1005,7 +1032,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertFalse(controller.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is disabled.
@@ -1015,7 +1042,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_STOPPED);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is enabled.
@@ -1026,7 +1054,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit(STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -1047,7 +1076,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate an uncertain event from the primary. This will start the secondary, which will
@@ -1062,7 +1092,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate failure event from the secondary. This should just affect the secondary's state.
@@ -1074,7 +1104,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
// And a success event from the primary provider should cause the controller to make
@@ -1087,7 +1117,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
assertFalse(controller.isUncertaintyTimeoutSet());
@@ -1100,7 +1130,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
}
@@ -1121,7 +1151,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate an uncertain event from the primary. This will start the secondary, which will
@@ -1136,7 +1167,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate failure event from the secondary. This should just affect the secondary's state.
@@ -1148,7 +1179,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertUncertaintyTimeoutSet(testEnvironment, controller);
// Now signal a config change so that geo detection is disabled.
@@ -1158,7 +1189,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_STOPPED);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is enabled. Only the primary can be
@@ -1170,7 +1202,8 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -1191,7 +1224,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a failure event from the primary. This will start the secondary.
@@ -1203,7 +1237,7 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestMetricsLogger.assertStateChangesAndCommit();
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertNoEventReported();
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate failure event from the secondary.
@@ -1214,7 +1248,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_FAILED);
- mTestCallback.assertUncertainSuggestionMadeAndCommit();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
}
@@ -1233,7 +1268,7 @@ public class LocationTimeZoneProviderControllerTest {
{
LocationTimeZoneManagerServiceState state = controller.getStateForTests();
assertEquals(STATE_INITIALIZING, state.getControllerState());
- assertNull(state.getLastSuggestion());
+ assertNull(state.getLastEvent().getSuggestion());
assertControllerRecordedStates(state,
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
assertProviderStates(state.getPrimaryProviderStates(),
@@ -1251,7 +1286,7 @@ public class LocationTimeZoneProviderControllerTest {
{
LocationTimeZoneManagerServiceState state = controller.getStateForTests();
assertEquals(STATE_INITIALIZING, state.getControllerState());
- assertNull(state.getLastSuggestion());
+ assertNull(state.getLastEvent().getSuggestion());
assertControllerRecordedStates(state);
assertProviderStates(
state.getPrimaryProviderStates(), PROVIDER_STATE_STARTED_UNCERTAIN);
@@ -1268,7 +1303,7 @@ public class LocationTimeZoneProviderControllerTest {
LocationTimeZoneManagerServiceState state = controller.getStateForTests();
assertEquals(STATE_CERTAIN, state.getControllerState());
assertEquals(USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds(),
- state.getLastSuggestion().getZoneIds());
+ state.getLastEvent().getSuggestion().getZoneIds());
assertControllerRecordedStates(state, STATE_CERTAIN);
assertProviderStates(state.getPrimaryProviderStates());
assertProviderStates(
@@ -1280,7 +1315,7 @@ public class LocationTimeZoneProviderControllerTest {
LocationTimeZoneManagerServiceState state = controller.getStateForTests();
assertEquals(STATE_CERTAIN, state.getControllerState());
assertEquals(USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds(),
- state.getLastSuggestion().getZoneIds());
+ state.getLastEvent().getSuggestion().getZoneIds());
assertControllerRecordedStates(state);
assertProviderStates(state.getPrimaryProviderStates());
assertProviderStates(state.getSecondaryProviderStates());
@@ -1313,7 +1348,8 @@ public class LocationTimeZoneProviderControllerTest {
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(
STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
- mTestCallback.assertNoSuggestionMade();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_RUNNING);
assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate the primary provider suggesting a time zone.
@@ -1327,7 +1363,7 @@ public class LocationTimeZoneProviderControllerTest {
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
- mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+ mTestCallback.assertEventWithCertainSuggestionReportedAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
assertFalse(controller.isUncertaintyTimeoutSet());
@@ -1335,11 +1371,11 @@ public class LocationTimeZoneProviderControllerTest {
controller.destroy();
assertControllerState(controller, STATE_DESTROYED);
- mTestMetricsLogger.assertStateChangesAndCommit(
- STATE_UNCERTAIN, STATE_STOPPED, STATE_DESTROYED);
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_STOPPED, STATE_DESTROYED);
// Confirm that the previous suggestion was overridden.
- mTestCallback.assertUncertainSuggestionMadeAndCommit();
+ mTestCallback.assertEventWithNoSuggestionReportedAndCommit(
+ DETECTION_ALGORITHM_STATUS_NOT_RUNNING);
mTestPrimaryLocationTimeZoneProvider.assertStateChangesAndCommit(
PROVIDER_STATE_STOPPED, PROVIDER_STATE_DESTROYED);
@@ -1517,63 +1553,101 @@ public class LocationTimeZoneProviderControllerTest {
private static class TestCallback extends LocationTimeZoneProviderController.Callback {
- private TestState<GeolocationTimeZoneSuggestion> mLatestSuggestion = new TestState<>();
+ private TestState<LocationAlgorithmEvent> mLatestEvent = new TestState<>();
TestCallback(ThreadingDomain threadingDomain) {
super(threadingDomain);
}
@Override
- void suggest(GeolocationTimeZoneSuggestion suggestion) {
- mLatestSuggestion.set(suggestion);
+ void sendEvent(LocationAlgorithmEvent event) {
+ mLatestEvent.set(event);
+ }
+
+ void assertNoEventReported() {
+ mLatestEvent.assertHasNotBeenSet();
+ }
+
+ /**
+ * Asserts one or more events have been reported, and the most recent does not contain a
+ * suggestion.
+ */
+ void assertEventWithNoSuggestionReportedAndCommit(
+ @DetectionAlgorithmStatus int expectedAlgorithmStatus) {
+ mLatestEvent.assertHasBeenSet();
+
+ LocationAlgorithmEvent latest = mLatestEvent.getLatest();
+ assertEquals(expectedAlgorithmStatus, latest.getAlgorithmStatus().getStatus());
+ assertNull(latest.getSuggestion());
+ mLatestEvent.commitLatest();
}
- void assertCertainSuggestionMadeFromEventAndCommit(TimeZoneProviderEvent event) {
+ void assertEventWithCertainSuggestionReportedAndCommit(TimeZoneProviderEvent event) {
// Test coding error if this fails.
assertEquals(TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION, event.getType());
+ // By definition, the algorithm has to be running to report a suggestion.
+ @DetectionAlgorithmStatus int expectedAlgorithmStatus =
+ DETECTION_ALGORITHM_STATUS_RUNNING;
TimeZoneProviderSuggestion suggestion = event.getSuggestion();
- assertSuggestionMadeAndCommit(
+ assertEventWithSuggestionReportedAndCommit(
+ expectedAlgorithmStatus,
suggestion.getElapsedRealtimeMillis(),
suggestion.getTimeZoneIds());
}
- void assertNoSuggestionMade() {
- mLatestSuggestion.assertHasNotBeenSet();
- }
-
- /** Asserts that an uncertain suggestion has been made from the supplied event. */
- void assertUncertainSuggestionMadeFromEventAndCommit(TimeZoneProviderEvent event) {
+ /**
+ * Asserts that one or more events have been reported, and the most recent contains an
+ * uncertain suggestion matching select details from the supplied provider event.
+ */
+ void assertEventWithUncertainSuggestionReportedAndCommit(TimeZoneProviderEvent event) {
// Test coding error if this fails.
assertEquals(TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN, event.getType());
- assertSuggestionMadeAndCommit(event.getCreationElapsedMillis(), null);
+ // By definition, the algorithm has to be running to report a suggestion.
+ @DetectionAlgorithmStatus int expectedAlgorithmStatus =
+ DETECTION_ALGORITHM_STATUS_RUNNING;
+ assertEventWithSuggestionReportedAndCommit(
+ expectedAlgorithmStatus, event.getCreationElapsedMillis(), null);
}
/**
- * Asserts that an uncertain suggestion has been made.
- * Ignores the suggestion's effectiveFromElapsedMillis.
+ * Asserts that one or more events have been reported, and the most recent contains an
+ * uncertain suggestion. Ignores the suggestion's effectiveFromElapsedMillis.
*/
- void assertUncertainSuggestionMadeAndCommit() {
+ void assertEventWithUncertainSuggestionReportedAndCommit() {
+ // By definition, the algorithm has to be running to report a suggestion.
+ @DetectionAlgorithmStatus int expectedAlgorithmStatus =
+ DETECTION_ALGORITHM_STATUS_RUNNING;
+
// An "uncertain" suggestion has null time zone IDs.
- assertSuggestionMadeAndCommit(null, null);
+ assertEventWithSuggestionReportedAndCommit(expectedAlgorithmStatus, null, null);
}
/**
- * Asserts that a suggestion has been made and some properties of that suggestion.
- * When expectedEffectiveFromElapsedMillis is null then its value isn't checked.
+ * Asserts that an event has been reported containing a suggestion and some properties of
+ * that suggestion. When expectedEffectiveFromElapsedMillis is null then its value isn't
+ * checked.
*/
- private void assertSuggestionMadeAndCommit(
+ private void assertEventWithSuggestionReportedAndCommit(
+ @DetectionAlgorithmStatus int expectedAlgorithmStatus,
@Nullable @ElapsedRealtimeLong Long expectedEffectiveFromElapsedMillis,
@Nullable List<String> expectedZoneIds) {
- mLatestSuggestion.assertHasBeenSet();
+ mLatestEvent.assertHasBeenSet();
+
+ LocationAlgorithmEvent latestEvent = mLatestEvent.getLatest();
+ assertEquals(expectedAlgorithmStatus, latestEvent.getAlgorithmStatus().getStatus());
+
+ GeolocationTimeZoneSuggestion suggestion = latestEvent.getSuggestion();
+ assertNotNull("Latest event doesn't contain a suggestion: event=" + latestEvent,
+ suggestion);
+
if (expectedEffectiveFromElapsedMillis != null) {
- assertEquals(
- expectedEffectiveFromElapsedMillis.longValue(),
- mLatestSuggestion.getLatest().getEffectiveFromElapsedMillis());
+ assertEquals(expectedEffectiveFromElapsedMillis.longValue(),
+ suggestion.getEffectiveFromElapsedMillis());
}
- assertEquals(expectedZoneIds, mLatestSuggestion.getLatest().getZoneIds());
- mLatestSuggestion.commitLatest();
+ assertEquals(expectedZoneIds, suggestion.getZoneIds());
+ mLatestEvent.commitLatest();
}
}