Merge "Add status to TimeZoneCapabilitiesAndConfig"

This commit is contained in:
Neil Fuller
2022-11-17 09:28:35 +00:00
committed by Android (Google) Code Review
37 changed files with 2885 additions and 681 deletions

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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,25 +66,40 @@ 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.
*/
@@ -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
+ '}';
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}
/*

View File

@@ -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"));
}
}

View File

@@ -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()));
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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");
}
}

View File

@@ -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");
}
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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();

View File

@@ -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();

View File

@@ -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);
/**
@@ -207,6 +210,14 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
@GuardedBy("this")
@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
@@ -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.
// withdraw a previous suggestion, i.e. submit an event containing 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);
// 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);
}
}

View File

@@ -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;
}

View File

@@ -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: ");

View File

@@ -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() {

View File

@@ -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);
}
/**

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
GeolocationTimeZoneSuggestion uncertainSuggestion = createUncertainGeolocationSuggestion();
TimeZoneDetectorStatus expectedInitialDetectorStatus = new TimeZoneDetectorStatus(
DETECTOR_STATUS_RUNNING,
TELEPHONY_ALGORITHM_RUNNING_STATUS,
LocationTimeZoneAlgorithmStatus.UNKNOWN);
script.verifyCachedDetectorStatus(expectedInitialDetectorStatus);
script.simulateGeolocationTimeZoneSuggestion(uncertainSuggestion)
.verifyTimeZoneNotChanged();
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);
// Assert internal service state.
assertEquals(uncertainSuggestion,
mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
{
LocationAlgorithmEvent locationAlgorithmEvent =
new LocationAlgorithmEvent(algorithmStatus1, null);
script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged();
assertStateChangeNotificationsSent(stateChangeListener, 1);
// 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 testGeoSuggestion_noZones() {
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();
.resetConfigurationTracking()
.registerStateChangeListener(stateChangeListener);
GeolocationTimeZoneSuggestion noZonesSuggestion = createCertainGeolocationSuggestion();
script.simulateGeolocationTimeZoneSuggestion(noZonesSuggestion)
LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent();
script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged();
assertStateChangeNotificationsSent(stateChangeListener, 1);
// Assert internal service state.
assertEquals(noZonesSuggestion, 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_oneZone() {
GeolocationTimeZoneSuggestion suggestion =
createCertainGeolocationSuggestion("Europe/London");
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);
script.simulateGeolocationTimeZoneSuggestion(suggestion)
.verifyTimeZoneChangedAndReset(suggestion);
LocationAlgorithmEvent locationAlgorithmEvent = createCertainLocationAlgorithmEvent();
script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged();
assertStateChangeNotificationsSent(stateChangeListener, 1);
// Assert internal service state.
assertEquals(suggestion, 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();
assertStateChangeNotificationsSent(stateChangeListener, 0);
// Assert internal service state.
script.verifyCachedDetectorStatus(expectedDetectorStatus)
.verifyLatestLocationAlgorithmEventReceived(locationAlgorithmEvent);
}
@Test
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()
.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();
assertStateChangeNotificationsSent(stateChangeListener, 0);
// Assert internal service state.
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);
}
}

View File

@@ -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 assertCertainSuggestionMadeFromEventAndCommit(TimeZoneProviderEvent 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 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();
}
}