Add status to TimeZoneCapabilitiesAndConfig
Update LocationTimeZoneProviderController to report newly available
status information.
After this change, the location_time_zone_manager now informs the
time_zone_detector of status changes, not just suggestion changes. To
reflect this, the path from location_time_zone_manager to
time_zone_detector involves a new "LocationAlgorithmEvent" (which can
contain an optional suggestion) and not the suggestion directly.
In fact, location_time_zone_manager may over-notify (i.e. when neither
status nor suggestion have changed) to reflect internal state changes in
LocationTimeZoneProviderController, but the time_zone_detector can
easily recognize dupes and discard them while keeping the latest event
received for logging / debugging.
The reason for this change is so that time zone detector status
information can be added to TimeZoneCapabilitiesAndConfig for use by
SettingsUI in upcoming UI changes. The full structure is represented
(Overall detector -> Detection Algorithms -> Sub-components like time
zone providers) so that the UI folks can use what they want today.
The initial use is so that SettingsUI can show user alerts / warnings
about "bad" location settings that are blocking / degrading location
time zone provider behavior. This change is also included in this commit
so the whole end-to-end path is implemented.
When the detector status changes, the UI (TimeZoneListener) listeners
will now be informed.
Manual testing:
Various commands tried like:
adb shell cmd time_zone_detector handle_location_algorithm_event \
--status '"LocationTimeZoneAlgorithmStatus{mAlgorithmStatus=RUNNING, mPrimaryProviderStatus=NOT_READY, mPrimaryProviderReportedStatus=null, mSecondaryProviderStatus=NOT_READY, mSecondaryProviderReportedStatus=null}"'
Bug: 236624675
Test: See manual test instructions
Test: atest services/tests/servicestests/src/com/android/server/timedetector/
Test: atest services/tests/servicestests/src/com/android/server/timezonedetector/
Test: atest ./tests/tests/time/src/android/app/time/cts/
Test: atest core/tests/coretests/src/android/service/timezone/
Test: atest core/tests/coretests/src/android/service/time/
Change-Id: I50636bf75ad2f57ddeb1dce829678cf354b2a05a
This commit is contained in:
227
core/java/android/app/time/DetectorStatusTypes.java
Normal file
227
core/java/android/app/time/DetectorStatusTypes.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
363
core/java/android/app/time/LocationTimeZoneAlgorithmStatus.java
Normal file
363
core/java/android/app/time/LocationTimeZoneAlgorithmStatus.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
+ '}';
|
||||
}
|
||||
|
||||
19
core/java/android/app/time/TimeZoneDetectorStatus.aidl
Normal file
19
core/java/android/app/time/TimeZoneDetectorStatus.aidl
Normal 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;
|
||||
124
core/java/android/app/time/TimeZoneDetectorStatus.java
Normal file
124
core/java/android/app/time/TimeZoneDetectorStatus.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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: ");
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user