Merge "Add support for tz detection telephony fallback"

This commit is contained in:
Neil Fuller
2021-11-25 16:05:56 +00:00
committed by Android (Google) Code Review
19 changed files with 696 additions and 15 deletions

View File

@@ -95,6 +95,13 @@ public interface TimeZoneDetector {
*/
String SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE = "suggest_telephony_time_zone";
/**
* A shell command that enables telephony time zone fallback. See {@link
* com.android.server.timezonedetector.TimeZoneDetectorStrategy} for details.
* @hide
*/
String SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK = "enable_telephony_fallback";
/**
* A shared utility method to create a {@link ManualTimeZoneSuggestion}.
*

View File

@@ -1802,6 +1802,13 @@
provider services. -->
<string name="config_secondaryLocationTimeZoneProviderPackageName" translatable="false"></string>
<!-- Whether the time zone detection logic supports fall back from geolocation suggestions to
telephony suggestions temporarily in certain circumstances. Reduces time zone detection
latency during some scenarios like air travel. Only useful when both geolocation and
telephony time zone detection are supported on a device.
See com.android.server.timezonedetector.TimeZoneDetectorStrategy for more information. -->
<bool name="config_supportTelephonyTimeZoneFallback" translatable="false">false</bool>
<!-- Whether to enable network location overlay which allows network location provider to be
replaced by an app at run-time. When disabled, only the
config_networkLocationProviderPackageName package will be searched for network location

View File

@@ -2238,6 +2238,7 @@
<java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" />
<java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneProvider" />
<java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" />
<java-symbol type="bool" name="config_supportTelephonyTimeZoneFallback" />
<java-symbol type="bool" name="config_autoResetAirplaneMode" />
<java-symbol type="string" name="config_notificationAccessConfirmationActivity" />
<java-symbol type="bool" name="config_killableInputMethods" />

View File

@@ -65,6 +65,7 @@ public final class ServerFlags {
KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE,
KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE,
KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED,
})
@Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
@Retention(RetentionPolicy.SOURCE)
@@ -138,6 +139,14 @@ public final class ServerFlags {
KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT =
"location_time_zone_detection_setting_enabled_default";
/**
* The key to control support for time zone detection falling back to telephony detection under
* certain circumstances.
*/
public static final @DeviceConfigKey String
KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED =
"time_zone_detector_telephony_fallback_supported";
/**
* The key to override the time detector origin priorities configuration. A comma-separated list
* of strings that will be passed to {@link TimeDetectorStrategy#stringToOrigin(String)}.

View File

@@ -39,6 +39,7 @@ public final class ConfigurationInternal {
private final boolean mTelephonyDetectionSupported;
private final boolean mGeoDetectionSupported;
private final boolean mTelephonyFallbackSupported;
private final boolean mAutoDetectionEnabledSetting;
private final @UserIdInt int mUserId;
private final boolean mUserConfigAllowed;
@@ -48,6 +49,7 @@ public final class ConfigurationInternal {
private ConfigurationInternal(Builder builder) {
mTelephonyDetectionSupported = builder.mTelephonyDetectionSupported;
mGeoDetectionSupported = builder.mGeoDetectionSupported;
mTelephonyFallbackSupported = builder.mTelephonyFallbackSupported;
mAutoDetectionEnabledSetting = builder.mAutoDetectionEnabledSetting;
mUserId = builder.mUserId;
@@ -71,6 +73,14 @@ public final class ConfigurationInternal {
return mGeoDetectionSupported;
}
/**
* Returns true if the device supports time zone detection falling back to telephony detection
* under certain circumstances.
*/
public boolean isTelephonyFallbackSupported() {
return mTelephonyFallbackSupported;
}
/** Returns the value of the auto time zone detection enabled setting. */
public boolean getAutoDetectionEnabledSetting() {
return mAutoDetectionEnabledSetting;
@@ -216,6 +226,7 @@ public final class ConfigurationInternal {
&& mUserConfigAllowed == that.mUserConfigAllowed
&& mTelephonyDetectionSupported == that.mTelephonyDetectionSupported
&& mGeoDetectionSupported == that.mGeoDetectionSupported
&& mTelephonyFallbackSupported == that.mTelephonyFallbackSupported
&& mAutoDetectionEnabledSetting == that.mAutoDetectionEnabledSetting
&& mLocationEnabledSetting == that.mLocationEnabledSetting
&& mGeoDetectionEnabledSetting == that.mGeoDetectionEnabledSetting;
@@ -224,8 +235,8 @@ public final class ConfigurationInternal {
@Override
public int hashCode() {
return Objects.hash(mUserId, mUserConfigAllowed, mTelephonyDetectionSupported,
mGeoDetectionSupported, mAutoDetectionEnabledSetting, mLocationEnabledSetting,
mGeoDetectionEnabledSetting);
mGeoDetectionSupported, mTelephonyFallbackSupported, mAutoDetectionEnabledSetting,
mLocationEnabledSetting, mGeoDetectionEnabledSetting);
}
@Override
@@ -235,6 +246,7 @@ public final class ConfigurationInternal {
+ ", mUserConfigAllowed=" + mUserConfigAllowed
+ ", mTelephonyDetectionSupported=" + mTelephonyDetectionSupported
+ ", mGeoDetectionSupported=" + mGeoDetectionSupported
+ ", mTelephonyFallbackSupported=" + mTelephonyFallbackSupported
+ ", mAutoDetectionEnabledSetting=" + mAutoDetectionEnabledSetting
+ ", mLocationEnabledSetting=" + mLocationEnabledSetting
+ ", mGeoDetectionEnabledSetting=" + mGeoDetectionEnabledSetting
@@ -251,6 +263,7 @@ public final class ConfigurationInternal {
private boolean mUserConfigAllowed;
private boolean mTelephonyDetectionSupported;
private boolean mGeoDetectionSupported;
private boolean mTelephonyFallbackSupported;
private boolean mAutoDetectionEnabledSetting;
private boolean mLocationEnabledSetting;
private boolean mGeoDetectionEnabledSetting;
@@ -269,6 +282,7 @@ public final class ConfigurationInternal {
this.mUserId = toCopy.mUserId;
this.mUserConfigAllowed = toCopy.mUserConfigAllowed;
this.mTelephonyDetectionSupported = toCopy.mTelephonyDetectionSupported;
this.mTelephonyFallbackSupported = toCopy.mTelephonyFallbackSupported;
this.mGeoDetectionSupported = toCopy.mGeoDetectionSupported;
this.mAutoDetectionEnabledSetting = toCopy.mAutoDetectionEnabledSetting;
this.mLocationEnabledSetting = toCopy.mLocationEnabledSetting;
@@ -299,6 +313,15 @@ public final class ConfigurationInternal {
return this;
}
/**
* Sets whether time zone detection supports falling back to telephony detection under
* certain circumstances.
*/
public Builder setTelephonyFallbackSupported(boolean supported) {
mTelephonyFallbackSupported = supported;
return this;
}
/**
* Sets the value of the automatic time zone detection enabled setting for this device.
*/

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.timezonedetector;
import android.annotation.NonNull;
/**
* The interface for the class that is responsible for detecting device activities relevant to
* time zone detection. This interface exists to decouple parts of the time zone detector from each
* other and to enable easier testing.
*
* @hide
*/
interface DeviceActivityMonitor extends Dumpable {
/** Adds a listener. */
void addListener(@NonNull Listener listener);
/**
* A listener for device activities. See {@link DeviceActivityMonitor#addListener(Listener)}.
*/
interface Listener {
/** A flight has completed. */
void onFlightComplete();
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.timezonedetector;
import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* The real implementation of {@link DeviceActivityMonitor}.
*/
class DeviceActivityMonitorImpl implements DeviceActivityMonitor {
private static final String LOG_TAG = TimeZoneDetectorService.TAG;
private static final boolean DBG = TimeZoneDetectorService.DBG;
static DeviceActivityMonitor create(@NonNull Context context, @NonNull Handler handler) {
return new DeviceActivityMonitorImpl(context, handler);
}
@GuardedBy("this")
@NonNull
private final List<Listener> mListeners = new ArrayList<>();
private DeviceActivityMonitorImpl(@NonNull Context context, @NonNull Handler handler) {
// The way this "detects" a flight concluding is by the user explicitly turning off airplane
// mode. Smarter heuristics would be nice.
ContentResolver contentResolver = context.getContentResolver();
ContentObserver airplaneModeObserver = new ContentObserver(handler) {
@Override
public void onChange(boolean unused) {
try {
int state = Settings.Global.getInt(
contentResolver, Settings.Global.AIRPLANE_MODE_ON);
if (state == 0) {
notifyFlightComplete();
}
} catch (Settings.SettingNotFoundException e) {
Slog.e(LOG_TAG, "Unable to read airplane mode state", e);
}
}
};
contentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
true /* notifyForDescendants */,
airplaneModeObserver);
}
@Override
public synchronized void addListener(Listener listener) {
Objects.requireNonNull(listener);
mListeners.add(listener);
}
private synchronized void notifyFlightComplete() {
if (DBG) {
Slog.d(LOG_TAG, "notifyFlightComplete");
}
for (Listener listener : mListeners) {
listener.onFlightComplete();
}
}
@Override
public void dump(IndentingPrintWriter pw, String[] args) {
// No-op right now: no state to dump.
}
}

View File

@@ -16,11 +16,13 @@
package com.android.server.timezonedetector;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.content.Context;
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
import java.util.Objects;
@@ -79,4 +81,9 @@ final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment
AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
alarmManager.setTimeZone(zoneId);
}
@Override
public @ElapsedRealtimeLong long elapsedRealtimeMillis() {
return SystemClock.elapsedRealtime();
}
}

View File

@@ -111,6 +111,11 @@ public final class MetricsTimeZoneDetectorState {
return mConfigurationInternal.isGeoDetectionSupported();
}
/** Returns true if the device supports telephony time zone detection fallback. */
public boolean isTelephonyTimeZoneFallbackSupported() {
return mConfigurationInternal.isTelephonyFallbackSupported();
}
/** Returns true if user's location can be used generally. */
public boolean getUserLocationEnabledSetting() {
return mConfigurationInternal.getLocationEnabledSetting();

View File

@@ -64,6 +64,7 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
ServerFlags.KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED,
}));
/**
@@ -294,6 +295,7 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
.setTelephonyDetectionFeatureSupported(
isTelephonyTimeZoneDetectionFeatureSupported())
.setGeoDetectionFeatureSupported(isGeoTimeZoneDetectionFeatureSupported())
.setTelephonyFallbackSupported(isTelephonyFallbackSupported())
.setAutoDetectionEnabledSetting(getAutoDetectionEnabledSetting())
.setUserConfigAllowed(isUserConfigAllowed(userId))
.setLocationEnabledSetting(getLocationEnabledSetting(userId))
@@ -549,6 +551,13 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
mRecordProviderStateChanges = false;
}
private boolean isTelephonyFallbackSupported() {
return mServerFlags.getBoolean(
ServerFlags.KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED,
getConfigBoolean(
com.android.internal.R.bool.config_supportTelephonyTimeZoneFallback));
}
private boolean getConfigBoolean(int providerEnabledConfigId) {
Resources resources = mContext.getResources();
return resources.getBoolean(providerEnabledConfigId);

View File

@@ -83,6 +83,16 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
ServiceConfigAccessorImpl.getInstance(context);
TimeZoneDetectorStrategy timeZoneDetectorStrategy =
TimeZoneDetectorStrategyImpl.create(context, handler, serviceConfigAccessor);
DeviceActivityMonitor deviceActivityMonitor =
DeviceActivityMonitorImpl.create(context, handler);
// Wire up the telephony fallback behavior to activity detection.
deviceActivityMonitor.addListener(new DeviceActivityMonitor.Listener() {
@Override
public void onFlightComplete() {
timeZoneDetectorStrategy.enableTelephonyTimeZoneFallback();
}
});
// Create and publish the local service for use by internal callers.
TimeZoneDetectorInternal internal =
@@ -93,6 +103,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
// permissioned) processes.
TimeZoneDetectorService service = TimeZoneDetectorService.create(
context, handler, serviceConfigAccessor, timeZoneDetectorStrategy);
// Dump the device activity monitor when the service is dumped.
service.addDumpable(deviceActivityMonitor);
publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service);
}
}
@@ -331,6 +345,15 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
return mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported();
}
/**
* Sends a signal to enable telephony fallback. Provided for command-line access for use
* during tests. This is not exposed as a binder API.
*/
void enableTelephonyFallback() {
enforceManageTimeZoneDetectorPermission();
mTimeZoneDetectorStrategy.enableTelephonyTimeZoneFallback();
}
/**
* Registers the supplied {@link Dumpable} for dumping. When the service is dumped
* {@link Dumpable#dump(IndentingPrintWriter, String[])} will be called on the {@code dumpable}.

View File

@@ -15,6 +15,7 @@
*/
package com.android.server.timezonedetector;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK;
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;
@@ -30,6 +31,7 @@ import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE;
import static com.android.server.timedetector.ServerFlags.KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED;
import android.app.time.LocationTimeZoneManager;
import android.app.time.TimeZoneConfiguration;
@@ -76,6 +78,8 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
return runSuggestManualTimeZone();
case SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE:
return runSuggestTelephonyTimeZone();
case SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK:
return runEnableTelephonyFallback();
default: {
return handleDefaultCommands(cmd);
}
@@ -169,6 +173,11 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
}
}
private int runEnableTelephonyFallback() {
mInterface.enableTelephonyFallback();
return 1;
}
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -190,6 +199,13 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
+ "\n");
pw.printf(" %s true|false\n", SHELL_COMMAND_SET_GEO_DETECTION_ENABLED);
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(" %s <manual suggestion opts>\n",
@@ -216,6 +232,9 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
pw.printf(" %s\n", KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE);
pw.printf(" Used to override the device's 'geolocation time zone detection enabled'"
+ " setting [*].\n");
pw.printf(" %s\n", KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED);
pw.printf(" Used to enable / disable support for telephony detection fallback. Also see"
+ " the %s command.\n", SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK);
pw.println();
pw.printf("[*] To be enabled, the user must still have location = on / auto time zone"
+ " detection = on.\n");

View File

@@ -69,6 +69,20 @@ import android.util.IndentingPrintWriter;
* users enter areas without the necessary signals. Ultimately, with no perfect algorithm available,
* the user is left to choose which algorithm works best for their circumstances.
*
* <p>When geolocation detection is supported and enabled, in certain circumstances, such as during
* international travel, it makes sense to prioritize speed of detection via telephony (when
* available) Vs waiting for the geolocation algorithm to reach certainty. Geolocation detection can
* sometimes be slow to get a location fix and can require network connectivity (which cannot be
* assumed when users are travelling) for server-assisted location detection or time zone lookup.
* Therefore, as a restricted form of prioritization between geolocation and telephony algorithms,
* the strategy provides "telephony fallback" behavior, which can be set to "supported" via device
* config. Fallback mode is toggled on at runtime via {@link #enableTelephonyTimeZoneFallback()} in
* response to signals outside of the scope of this class. Telephony fallback allows the use of
* telephony suggestions to help with faster detection but only until geolocation detection
* provides a concrete, "certain" suggestion. After geolocation has made the first certain
* suggestion, telephony fallback is disabled until the next call to {@link
* #enableTelephonyTimeZoneFallback()}.
*
* <p>Threading:
*
* <p>Implementations of this class must be thread-safe as calls calls like {@link
@@ -100,6 +114,13 @@ public interface TimeZoneDetectorStrategy extends Dumpable {
*/
void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion);
/**
* Tells the strategy that it can fall back to telephony detection while geolocation detection
* remains uncertain. {@link #suggestGeolocationTimeZone(GeolocationTimeZoneSuggestion)} can
* disable it again. See {@link TimeZoneDetectorStrategy} for details.
*/
void enableTelephonyTimeZoneFallback();
/** Generates a state snapshot for metrics. */
@NonNull
MetricsTimeZoneDetectorState generateMetricsState();

View File

@@ -22,6 +22,7 @@ import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_M
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -31,6 +32,7 @@ import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.content.Context;
import android.os.Handler;
import android.os.TimestampedValue;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.Slog;
@@ -38,6 +40,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.time.Duration;
import java.util.List;
import java.util.Objects;
@@ -83,6 +86,13 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
* Sets the device's time zone.
*/
void setDeviceTimeZone(@NonNull String zoneId);
/**
* Returns the time according to the elapsed realtime clock, the same as {@link
* android.os.SystemClock#elapsedRealtime()}.
*/
@ElapsedRealtimeLong
long elapsedRealtimeMillis();
}
private static final String LOG_TAG = TimeZoneDetectorService.TAG;
@@ -190,6 +200,21 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
@NonNull
private ConfigurationInternal mCurrentConfigurationInternal;
/**
* Whether telephony time zone detection fallback is currently enabled (when device config also
* allows).
*
* <p>This field is only actually used when telephony time zone fallback is supported, but the
* value is maintained even when it isn't supported as it can be turned on at any time via
* server flags. The reference time is the elapsed realtime when the mode last changed to help
* ordering between fallback mode switches and suggestions.
*
* <p>See {@link TimeZoneDetectorStrategy} for more information.
*/
@GuardedBy("this")
@NonNull
private TimestampedValue<Boolean> mTelephonyTimeZoneFallbackEnabled;
/**
* Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
*/
@@ -205,6 +230,10 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
public TimeZoneDetectorStrategyImpl(@NonNull Environment environment) {
mEnvironment = Objects.requireNonNull(environment);
// Start with telephony fallback enabled.
mTelephonyTimeZoneFallbackEnabled =
new TimestampedValue<>(mEnvironment.elapsedRealtimeMillis(), true);
synchronized (this) {
mEnvironment.setConfigurationInternalChangeListener(
this::handleConfigurationInternalChanged);
@@ -233,6 +262,10 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
// are made in a sensible order and the most recent is always the best one to use.
mLatestGeoLocationSuggestion.set(suggestion);
// Update the mTelephonyTimeZoneFallbackEnabled state if needed: a certain suggestion
// will usually disable telephony fallback mode if it is currently enabled.
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;
@@ -303,6 +336,43 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
doAutoTimeZoneDetection(currentUserConfig, reason);
}
@Override
public synchronized void enableTelephonyTimeZoneFallback() {
// Only do any work if fallback is currently not enabled.
if (!mTelephonyTimeZoneFallbackEnabled.getValue()) {
ConfigurationInternal currentUserConfig = mCurrentConfigurationInternal;
if (DBG) {
Slog.d(LOG_TAG, "enableTelephonyTimeZoneFallbackMode"
+ ": currentUserConfig=" + currentUserConfig);
}
final boolean fallbackEnabled = true;
mTelephonyTimeZoneFallbackEnabled = new TimestampedValue<>(
mEnvironment.elapsedRealtimeMillis(), fallbackEnabled);
// mTelephonyTimeZoneFallbackEnabled and mLatestGeoLocationSuggestion interact.
// If there is currently 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
// enabled. This comment / call is left as a reminder that it is possible for there to
// be a current, "certain" geolocation suggestion when this signal arrives and it is
// intentional that fallback stays enabled in this case. The choice to do this
// is mostly for symmetry WRT the case where fallback is enabled and an old "certain"
// geolocation is received; that would also leave telephony fallback enabled.
// This choice means that telephony fallback will remain enabled until a new "certain"
// geolocation suggestion is received. If, instead, the next geolocation is "uncertain",
// then telephony fallback will occur.
disableTelephonyFallbackIfNeeded();
if (currentUserConfig.isTelephonyFallbackSupported()) {
String reason = "enableTelephonyTimeZoneFallbackMode";
doAutoTimeZoneDetection(currentUserConfig, reason);
}
}
}
@Override
@NonNull
public synchronized MetricsTimeZoneDetectorState generateMetricsState() {
@@ -361,7 +431,32 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
// Use the correct algorithm based on the user's current configuration. If it changes, then
// detection will be re-run.
if (currentUserConfig.getGeoDetectionEnabledBehavior()) {
doGeolocationTimeZoneDetection(detectionReason);
boolean isGeoDetectionCertain = doGeolocationTimeZoneDetection(detectionReason);
// When geolocation detection is uncertain of the time zone, telephony detection
// can be used if telephony fallback is enabled and supported.
if (!isGeoDetectionCertain
&& mTelephonyTimeZoneFallbackEnabled.getValue()
&& currentUserConfig.isTelephonyFallbackSupported()) {
// This "only look at telephony if geolocation is uncertain" approach is
// deliberate to try to keep the logic simple and keep telephony and geolocation
// detection decoupled: when geolocation detection is in use, it is fully
// trusted and the most recent "certain" geolocation suggestion available will
// be used, even if the information it is based on is quite old.
// There could be newer telephony suggestions available, but telephony
// suggestions tend not to be withdrawn when they should be, and are based on
// combining information like MCC and NITZ signals, which could have been
// received at different times; thus it is hard to say what time the suggestion
// is actually "for" and reason clearly about ordering between telephony and
// geolocation suggestions.
//
// This approach is reliant on the location_time_zone_manager (and the location
// time zone providers it manages) correctly sending "uncertain" suggestions
// when the current location is unknown so that telephony fallback will actually be
// used.
doTelephonyTimeZoneDetection(detectionReason + ", telephony fallback mode");
}
} else {
doTelephonyTimeZoneDetection(detectionReason);
}
@@ -371,21 +466,29 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
* Detects the time zone using the latest available geolocation time zone suggestion, if one is
* available. The outcome can be that this strategy becomes / remains un-opinionated and nothing
* is set.
*
* @return true if geolocation time zone detection was certain of the time zone, false if it is
* uncertain
*/
@GuardedBy("this")
private void doGeolocationTimeZoneDetection(@NonNull String detectionReason) {
private boolean doGeolocationTimeZoneDetection(@NonNull String detectionReason) {
GeolocationTimeZoneSuggestion latestGeolocationSuggestion =
mLatestGeoLocationSuggestion.get();
if (latestGeolocationSuggestion == null) {
return;
return false;
}
List<String> zoneIds = latestGeolocationSuggestion.getZoneIds();
if (zoneIds == null || zoneIds.isEmpty()) {
// This means the client has become uncertain about the time zone or it is certain there
// is no known zone. In either case we must leave the existing time zone setting as it
// is.
return;
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
// a different answer elsewhere.
return false;
} else if (zoneIds.isEmpty()) {
// This means the originator is certain there is no time zone. The existing time zone
// setting must be left as it is and detection must not go looking for a different
// answer elsewhere.
return true;
}
// GeolocationTimeZoneSuggestion has no measure of quality. We assume all suggestions are
@@ -404,6 +507,34 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
zoneId = zoneIds.get(0);
}
setDeviceTimeZoneIfRequired(zoneId, detectionReason);
return true;
}
/**
* 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.
*/
@GuardedBy("this")
private void disableTelephonyFallbackIfNeeded() {
GeolocationTimeZoneSuggestion suggestion = mLatestGeoLocationSuggestion.get();
boolean isLatestSuggestionCertain = suggestion != null && suggestion.getZoneIds() != null;
if (isLatestSuggestionCertain && mTelephonyTimeZoneFallbackEnabled.getValue()) {
// This transition ONLY changes mTelephonyTimeZoneFallbackEnabled from
// true -> false. See mTelephonyTimeZoneFallbackEnabled javadocs for details.
// Telephony fallback will be disabled after a "certain" suggestion is processed
// if and only if the location information it is based on is from after telephony
// fallback was enabled.
boolean latestSuggestionIsNewerThanFallbackEnabled =
suggestion.getEffectiveFromElapsedMillis()
> mTelephonyTimeZoneFallbackEnabled.getReferenceTimeMillis();
if (latestSuggestionIsNewerThanFallbackEnabled) {
final boolean fallbackEnabled = false;
mTelephonyTimeZoneFallbackEnabled = new TimestampedValue<>(
mEnvironment.elapsedRealtimeMillis(), fallbackEnabled);
}
}
}
/**
@@ -556,6 +687,12 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
+ mEnvironment.isDeviceTimeZoneInitialized());
ipw.println("mEnvironment.getDeviceTimeZone()=" + mEnvironment.getDeviceTimeZone());
ipw.println("Misc state:");
ipw.increaseIndent(); // level 2
ipw.println("mTelephonyTimeZoneFallbackEnabled="
+ formatDebugString(mTelephonyTimeZoneFallbackEnabled));
ipw.decreaseIndent(); // level 2
ipw.println("Time zone change log:");
ipw.increaseIndent(); // level 2
mTimeZoneChangesLog.dump(ipw);
@@ -603,6 +740,11 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
return mLatestGeoLocationSuggestion.get();
}
@VisibleForTesting
public synchronized boolean isTelephonyFallbackEnabledForTests() {
return mTelephonyTimeZoneFallbackEnabled.getValue();
}
/**
* A {@link TelephonyTimeZoneSuggestion} with additional qualifying metadata.
*/
@@ -652,4 +794,8 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
+ '}';
}
}
private static String formatDebugString(TimestampedValue<?> value) {
return value.getValue() + " @ " + Duration.ofMillis(value.getReferenceTimeMillis());
}
}

View File

@@ -48,6 +48,7 @@ public class ConfigurationInternalTest {
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
@@ -110,6 +111,7 @@ public class ConfigurationInternalTest {
.setUserConfigAllowed(false)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
@@ -174,6 +176,7 @@ public class ConfigurationInternalTest {
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(false)
.setGeoDetectionFeatureSupported(false)
.setTelephonyFallbackSupported(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
@@ -236,6 +239,7 @@ public class ConfigurationInternalTest {
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(false)
.setTelephonyFallbackSupported(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
@@ -288,4 +292,18 @@ public class ConfigurationInternalTest {
assertTrue(configuration.isGeoDetectionEnabled());
}
}
@Test
public void test_telephonyFallbackSupported() {
ConfigurationInternal config = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(false)
.setTelephonyFallbackSupported(true)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
.build();
assertTrue(config.isTelephonyFallbackSupported());
}
}

View File

@@ -50,6 +50,11 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
mLastTelephonySuggestion = timeZoneSuggestion;
}
@Override
public void enableTelephonyTimeZoneFallback() {
throw new UnsupportedOperationException();
}
@Override
public MetricsTimeZoneDetectorState generateMetricsState() {
throw new UnsupportedOperationException();

View File

@@ -378,6 +378,7 @@ public class TimeZoneDetectorServiceTest {
return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setUserConfigAllowed(true)
.setAutoDetectionEnabledSetting(autoDetectionEnabled)
.setLocationEnabledSetting(geoDetectionEnabled)

View File

@@ -60,6 +60,7 @@ import java.util.function.Function;
public class TimeZoneDetectorStrategyImplTest {
private static final @UserIdInt int USER_ID = 9876;
private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234;
/** A time zone used for initialization that does not occur elsewhere in tests. */
private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
private static final int SLOT_INDEX1 = 10000;
@@ -89,6 +90,7 @@ public class TimeZoneDetectorStrategyImplTest {
.setUserConfigAllowed(false)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setAutoDetectionEnabledSetting(false)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(false)
@@ -99,6 +101,7 @@ public class TimeZoneDetectorStrategyImplTest {
.setUserConfigAllowed(false)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
@@ -109,6 +112,7 @@ public class TimeZoneDetectorStrategyImplTest {
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(false)
.setGeoDetectionFeatureSupported(false)
.setTelephonyFallbackSupported(false)
.setAutoDetectionEnabledSetting(false)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(false)
@@ -119,6 +123,7 @@ public class TimeZoneDetectorStrategyImplTest {
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setAutoDetectionEnabledSetting(false)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(false)
@@ -128,6 +133,7 @@ public class TimeZoneDetectorStrategyImplTest {
new ConfigurationInternal.Builder(USER_ID)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setUserConfigAllowed(true)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
@@ -138,6 +144,7 @@ public class TimeZoneDetectorStrategyImplTest {
new ConfigurationInternal.Builder(USER_ID)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setUserConfigAllowed(true)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
@@ -147,9 +154,6 @@ public class TimeZoneDetectorStrategyImplTest {
private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
private FakeEnvironment mFakeEnvironment;
// A fake source of time for suggestions. This will typically be incremented after every use.
@ElapsedRealtimeLong private long mElapsedRealtimeMillis;
@Before
public void setUp() {
mFakeEnvironment = new FakeEnvironment();
@@ -752,6 +756,204 @@ public class TimeZoneDetectorStrategyImplTest {
mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
}
@Test
public void testTelephonyFallback() {
ConfigurationInternal config = new ConfigurationInternal.Builder(
CONFIG_AUTO_ENABLED_GEO_ENABLED)
.setTelephonyFallbackSupported(true)
.build();
Script script = new Script()
.initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
.simulateConfigurationInternalChange(config)
.resetConfigurationTracking();
// Confirm initial state is as expected.
script.verifyTelephonyFallbackIsEnabled(true)
.verifyTimeZoneNotChanged();
// Although geolocation detection is enabled, telephony fallback should be used initially
// and until a suitable "certain" geolocation suggestion is received.
{
TelephonyTimeZoneSuggestion telephonySuggestion = createTelephonySuggestion(
SLOT_INDEX1, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
"Europe/Paris");
script.simulateIncrementClock()
.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
.verifyTimeZoneChangedAndReset(telephonySuggestion)
.verifyTelephonyFallbackIsEnabled(true);
}
// Receiving an "uncertain" geolocation suggestion should have no effect.
{
GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
createUncertainGeolocationSuggestion();
script.simulateIncrementClock()
.simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(true);
}
// Receiving a "certain" geolocation suggestion should disable telephony fallback mode.
{
GeolocationTimeZoneSuggestion geolocationSuggestion =
createCertainGeolocationSuggestion("Europe/London");
script.simulateIncrementClock()
.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
.verifyTimeZoneChangedAndReset(geolocationSuggestion)
.verifyTelephonyFallbackIsEnabled(false);
}
// Used to record the last telephony suggestion received, which will be used when fallback
// takes place.
TelephonyTimeZoneSuggestion lastTelephonySuggestion;
// Telephony suggestions should now be ignored and geolocation detection is "in control".
{
TelephonyTimeZoneSuggestion telephonySuggestion = createTelephonySuggestion(
SLOT_INDEX1, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
"Europe/Berlin");
script.simulateIncrementClock()
.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false);
lastTelephonySuggestion = telephonySuggestion;
}
// 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");
script.simulateIncrementClock()
.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
.verifyTimeZoneChangedAndReset(geolocationSuggestion)
.verifyTelephonyFallbackIsEnabled(false);
GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
createUncertainGeolocationSuggestion();
script.simulateIncrementClock()
.simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false);
script.simulateIncrementClock()
.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
// No change needed, device will already be set to Europe/Rome.
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false);
}
// Enable telephony fallback. Nothing will change, because the geolocation is still certain,
// but fallback will remain enabled.
{
script.simulateIncrementClock()
.simulateEnableTelephonyFallback()
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(true);
}
// Make the geolocation algorithm uncertain.
{
GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
createUncertainGeolocationSuggestion();
script.simulateIncrementClock()
.simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
.verifyTimeZoneChangedAndReset(lastTelephonySuggestion)
.verifyTelephonyFallbackIsEnabled(true);
}
// Make the geolocation algorithm certain, disabling telephony fallback.
{
GeolocationTimeZoneSuggestion geolocationSuggestion =
createCertainGeolocationSuggestion("Europe/Lisbon");
script.simulateIncrementClock()
.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
.verifyTimeZoneChangedAndReset(geolocationSuggestion)
.verifyTelephonyFallbackIsEnabled(false);
}
// Demonstrate what happens when geolocation is uncertain when telephony fallback is
// enabled.
{
GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
createUncertainGeolocationSuggestion();
script.simulateIncrementClock()
.simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false)
.simulateEnableTelephonyFallback()
.verifyTimeZoneChangedAndReset(lastTelephonySuggestion)
.verifyTelephonyFallbackIsEnabled(true);
}
}
@Test
public void testTelephonyFallback_noTelephonySuggestionToFallBackTo() {
ConfigurationInternal config = new ConfigurationInternal.Builder(
CONFIG_AUTO_ENABLED_GEO_ENABLED)
.setTelephonyFallbackSupported(true)
.build();
Script script = new Script()
.initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
.simulateConfigurationInternalChange(config)
.resetConfigurationTracking();
// Confirm initial state is as expected.
script.verifyTelephonyFallbackIsEnabled(true)
.verifyTimeZoneNotChanged();
// Receiving an "uncertain" geolocation suggestion should have no effect.
{
GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
createUncertainGeolocationSuggestion();
script.simulateIncrementClock()
.simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(true);
}
// Make an uncertain geolocation suggestion, there is no telephony suggestion to fall back
// to
{
GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
createUncertainGeolocationSuggestion();
script.simulateIncrementClock()
.simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(true);
}
// Similar to the case above, but force a fallback attempt after making a "certain"
// geolocation suggestion.
// 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");
script.simulateIncrementClock()
.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
.verifyTimeZoneChangedAndReset(geolocationSuggestion)
.verifyTelephonyFallbackIsEnabled(false);
GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
createUncertainGeolocationSuggestion();
script.simulateIncrementClock()
.simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false);
script.simulateIncrementClock()
.simulateEnableTelephonyFallback()
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(true);
}
}
@Test
public void testGenerateMetricsState() {
ConfigurationInternal expectedInternalConfig = CONFIG_AUTO_DISABLED_GEO_DISABLED;
@@ -835,6 +1037,8 @@ public class TimeZoneDetectorStrategyImplTest {
assertEquals(config.isTelephonyDetectionSupported(),
actualState.isTelephonyDetectionSupported());
assertEquals(config.isGeoDetectionSupported(), actualState.isGeoDetectionSupported());
assertEquals(config.isTelephonyFallbackSupported(),
actualState.isTelephonyTimeZoneFallbackSupported());
assertEquals(config.getAutoDetectionEnabledSetting(),
actualState.getAutoDetectionEnabledSetting());
assertEquals(config.getGeoDetectionEnabledSetting(),
@@ -865,7 +1069,7 @@ public class TimeZoneDetectorStrategyImplTest {
private GeolocationTimeZoneSuggestion createUncertainGeolocationSuggestion() {
return GeolocationTimeZoneSuggestion.createCertainSuggestion(
mElapsedRealtimeMillis++, null);
mFakeEnvironment.elapsedRealtimeMillis(), null);
}
private GeolocationTimeZoneSuggestion createCertainGeolocationSuggestion(
@@ -874,7 +1078,7 @@ public class TimeZoneDetectorStrategyImplTest {
GeolocationTimeZoneSuggestion suggestion =
GeolocationTimeZoneSuggestion.createCertainSuggestion(
mElapsedRealtimeMillis++, Arrays.asList(zoneIds));
mFakeEnvironment.elapsedRealtimeMillis(), Arrays.asList(zoneIds));
suggestion.addDebugInfo("Test suggestion");
return suggestion;
}
@@ -883,16 +1087,25 @@ public class TimeZoneDetectorStrategyImplTest {
private final TestState<String> mTimeZoneId = new TestState<>();
private ConfigurationInternal mConfigurationInternal;
private @ElapsedRealtimeLong long mElapsedRealtimeMillis;
private ConfigurationChangeListener mConfigurationInternalChangeListener;
void initializeConfig(ConfigurationInternal configurationInternal) {
mConfigurationInternal = configurationInternal;
}
void initializeClock(@ElapsedRealtimeLong long elapsedRealtimeMillis) {
mElapsedRealtimeMillis = elapsedRealtimeMillis;
}
void initializeTimeZoneSetting(String zoneId) {
mTimeZoneId.init(zoneId);
}
void incrementClock() {
mElapsedRealtimeMillis++;
}
@Override
public void setConfigurationInternalChangeListener(ConfigurationChangeListener listener) {
mConfigurationInternalChangeListener = listener;
@@ -936,6 +1149,12 @@ public class TimeZoneDetectorStrategyImplTest {
void commitAllChanges() {
mTimeZoneId.commitLatest();
}
@Override
@ElapsedRealtimeLong
public long elapsedRealtimeMillis() {
return mElapsedRealtimeMillis;
}
}
/**
@@ -949,6 +1168,16 @@ public class TimeZoneDetectorStrategyImplTest {
return this;
}
Script initializeClock(long elapsedRealtimeMillis) {
mFakeEnvironment.initializeClock(elapsedRealtimeMillis);
return this;
}
Script simulateIncrementClock() {
mFakeEnvironment.incrementClock();
return this;
}
/**
* Simulates the user / user's configuration changing.
*/
@@ -1008,6 +1237,15 @@ public class TimeZoneDetectorStrategyImplTest {
return this;
}
/**
* Simulates the time zone detection strategty receiving a signal that allows it to do
* telephony fallback.
*/
Script simulateEnableTelephonyFallback() {
mTimeZoneDetectorStrategy.enableTelephonyTimeZoneFallback();
return this;
}
/**
* Confirms that the device's time zone has not been set by previous actions since the test
* state was last reset.
@@ -1044,6 +1282,13 @@ public class TimeZoneDetectorStrategyImplTest {
return this;
}
/** Verifies the state for telephony fallback. */
Script verifyTelephonyFallbackIsEnabled(boolean expectedEnabled) {
assertEquals(expectedEnabled,
mTimeZoneDetectorStrategy.isTelephonyFallbackEnabledForTests());
return this;
}
Script resetConfigurationTracking() {
mFakeEnvironment.commitAllChanges();
return this;

View File

@@ -46,6 +46,7 @@ final class TestSupport {
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(geoDetectionEnabledSetting)