Merge "Change TimeZoneDetectorStrategyImpl to allow geo"

This commit is contained in:
Neil Fuller
2020-08-11 15:42:55 +00:00
committed by Android (Google) Code Review
14 changed files with 755 additions and 128 deletions

View File

@@ -91,11 +91,13 @@ public final class TimeZoneCapabilities implements Parcelable {
private final @UserIdInt int mUserId;
private final @CapabilityState int mConfigureAutoDetectionEnabled;
private final @CapabilityState int mConfigureGeoDetectionEnabled;
private final @CapabilityState int mSuggestManualTimeZone;
private TimeZoneCapabilities(@NonNull Builder builder) {
this.mUserId = builder.mUserId;
this.mConfigureAutoDetectionEnabled = builder.mConfigureAutoDetectionEnabled;
this.mConfigureGeoDetectionEnabled = builder.mConfigureGeoDetectionEnabled;
this.mSuggestManualTimeZone = builder.mSuggestManualTimeZone;
}
@@ -103,6 +105,7 @@ public final class TimeZoneCapabilities implements Parcelable {
private static TimeZoneCapabilities createFromParcel(Parcel in) {
return new TimeZoneCapabilities.Builder(in.readInt())
.setConfigureAutoDetectionEnabled(in.readInt())
.setConfigureGeoDetectionEnabled(in.readInt())
.setSuggestManualTimeZone(in.readInt())
.build();
}
@@ -111,6 +114,7 @@ public final class TimeZoneCapabilities implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mUserId);
dest.writeInt(mConfigureAutoDetectionEnabled);
dest.writeInt(mConfigureGeoDetectionEnabled);
dest.writeInt(mSuggestManualTimeZone);
}
@@ -120,8 +124,8 @@ public final class TimeZoneCapabilities implements Parcelable {
}
/**
* Returns the user's capability state for controlling automatic time zone detection via
* {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link
* Returns the user's capability state for controlling whether automatic time zone detection is
* enabled via {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link
* TimeZoneConfiguration#isAutoDetectionEnabled()}.
*/
@CapabilityState
@@ -129,6 +133,16 @@ public final class TimeZoneCapabilities implements Parcelable {
return mConfigureAutoDetectionEnabled;
}
/**
* Returns the user's capability state for controlling whether geolocation can be used to detect
* time zone via {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link
* TimeZoneConfiguration#isGeoDetectionEnabled()}.
*/
@CapabilityState
public int getConfigureGeoDetectionEnabled() {
return mConfigureGeoDetectionEnabled;
}
/**
* Returns the user's capability state for manually setting the time zone on a device via
* {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}.
@@ -157,12 +171,16 @@ public final class TimeZoneCapabilities implements Parcelable {
TimeZoneCapabilities that = (TimeZoneCapabilities) o;
return mUserId == that.mUserId
&& mConfigureAutoDetectionEnabled == that.mConfigureAutoDetectionEnabled
&& mConfigureGeoDetectionEnabled == that.mConfigureGeoDetectionEnabled
&& mSuggestManualTimeZone == that.mSuggestManualTimeZone;
}
@Override
public int hashCode() {
return Objects.hash(mUserId, mConfigureAutoDetectionEnabled, mSuggestManualTimeZone);
return Objects.hash(mUserId,
mConfigureAutoDetectionEnabled,
mConfigureGeoDetectionEnabled,
mSuggestManualTimeZone);
}
@Override
@@ -170,6 +188,7 @@ public final class TimeZoneCapabilities implements Parcelable {
return "TimeZoneDetectorCapabilities{"
+ "mUserId=" + mUserId
+ ", mConfigureAutomaticDetectionEnabled=" + mConfigureAutoDetectionEnabled
+ ", mConfigureGeoDetectionEnabled=" + mConfigureGeoDetectionEnabled
+ ", mSuggestManualTimeZone=" + mSuggestManualTimeZone
+ '}';
}
@@ -179,6 +198,7 @@ public final class TimeZoneCapabilities implements Parcelable {
private final @UserIdInt int mUserId;
private @CapabilityState int mConfigureAutoDetectionEnabled;
private @CapabilityState int mConfigureGeoDetectionEnabled;
private @CapabilityState int mSuggestManualTimeZone;
/**
@@ -194,6 +214,12 @@ public final class TimeZoneCapabilities implements Parcelable {
return this;
}
/** Sets the state for the geolocation time zone detection enabled config. */
public Builder setConfigureGeoDetectionEnabled(@CapabilityState int value) {
this.mConfigureGeoDetectionEnabled = value;
return this;
}
/** Sets the state for the suggestManualTimeZone action. */
public Builder setSuggestManualTimeZone(@CapabilityState int value) {
this.mSuggestManualTimeZone = value;
@@ -204,6 +230,7 @@ public final class TimeZoneCapabilities implements Parcelable {
@NonNull
public TimeZoneCapabilities build() {
verifyCapabilitySet(mConfigureAutoDetectionEnabled, "configureAutoDetectionEnabled");
verifyCapabilitySet(mConfigureGeoDetectionEnabled, "configureGeoDetectionEnabled");
verifyCapabilitySet(mSuggestManualTimeZone, "suggestManualTimeZone");
return new TimeZoneCapabilities(this);
}

View File

@@ -67,6 +67,10 @@ public final class TimeZoneConfiguration implements Parcelable {
@Property
public static final String PROPERTY_AUTO_DETECTION_ENABLED = "autoDetectionEnabled";
/** See {@link TimeZoneConfiguration#isGeoDetectionEnabled()} for details. */
@Property
public static final String PROPERTY_GEO_DETECTION_ENABLED = "geoDetectionEnabled";
private final Bundle mBundle;
private TimeZoneConfiguration(Builder builder) {
@@ -86,7 +90,8 @@ public final class TimeZoneConfiguration implements Parcelable {
/** Returns {@code true} if all known properties are set. */
public boolean isComplete() {
return hasProperty(PROPERTY_AUTO_DETECTION_ENABLED);
return hasProperty(PROPERTY_AUTO_DETECTION_ENABLED)
&& hasProperty(PROPERTY_GEO_DETECTION_ENABLED);
}
/** Returns true if the specified property is set. */
@@ -108,6 +113,28 @@ public final class TimeZoneConfiguration implements Parcelable {
return mBundle.getBoolean(PROPERTY_AUTO_DETECTION_ENABLED);
}
/**
* Returns the value of the {@link #PROPERTY_GEO_DETECTION_ENABLED} property. This
* controls whether a device can use location to determine time zone. Only used when
* {@link #isAutoDetectionEnabled()} is true.
*
* @throws IllegalStateException if the field has not been set
*/
public boolean isGeoDetectionEnabled() {
if (!mBundle.containsKey(PROPERTY_GEO_DETECTION_ENABLED)) {
throw new IllegalStateException(PROPERTY_GEO_DETECTION_ENABLED + " is not set");
}
return mBundle.getBoolean(PROPERTY_GEO_DETECTION_ENABLED);
}
/**
* Convenience method to merge this with another. The argument configuration properties have
* precedence.
*/
public TimeZoneConfiguration with(TimeZoneConfiguration other) {
return new Builder(this).mergeProperties(other).build();
}
@Override
public int describeContents() {
return 0;
@@ -174,6 +201,12 @@ public final class TimeZoneConfiguration implements Parcelable {
return this;
}
/** Sets the desired state of the geolocation time zone detection enabled property. */
public Builder setGeoDetectionEnabled(boolean enabled) {
this.mBundle.putBoolean(PROPERTY_GEO_DETECTION_ENABLED, enabled);
return this;
}
/** Returns the {@link TimeZoneConfiguration}. */
@NonNull
public TimeZoneConfiguration build() {

View File

@@ -33,9 +33,11 @@ public class TimeZoneCapabilitiesTest {
public void testEquals() {
TimeZoneCapabilities.Builder builder1 = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
.setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
.setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
.setSuggestManualTimeZone(CAPABILITY_POSSESSED);
TimeZoneCapabilities.Builder builder2 = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
.setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
.setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
.setSuggestManualTimeZone(CAPABILITY_POSSESSED);
{
TimeZoneCapabilities one = builder1.build();
@@ -57,6 +59,20 @@ public class TimeZoneCapabilitiesTest {
assertEquals(one, two);
}
builder2.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED);
{
TimeZoneCapabilities one = builder1.build();
TimeZoneCapabilities two = builder2.build();
assertNotEquals(one, two);
}
builder1.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED);
{
TimeZoneCapabilities one = builder1.build();
TimeZoneCapabilities two = builder2.build();
assertEquals(one, two);
}
builder2.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED);
{
TimeZoneCapabilities one = builder1.build();
@@ -76,12 +92,16 @@ public class TimeZoneCapabilitiesTest {
public void testParcelable() {
TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
.setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
.setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
.setSuggestManualTimeZone(CAPABILITY_POSSESSED);
assertRoundTripParcelable(builder.build());
builder.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED);
assertRoundTripParcelable(builder.build());
builder.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED);
assertRoundTripParcelable(builder.build());
builder.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED);
assertRoundTripParcelable(builder.build());
}

View File

@@ -29,8 +29,9 @@ public class TimeZoneConfigurationTest {
@Test
public void testBuilder_copyConstructor() {
TimeZoneConfiguration.Builder builder1 =
new TimeZoneConfiguration.Builder().setAutoDetectionEnabled(true);
TimeZoneConfiguration.Builder builder1 = new TimeZoneConfiguration.Builder()
.setAutoDetectionEnabled(true)
.setGeoDetectionEnabled(true);
TimeZoneConfiguration configuration1 = builder1.build();
TimeZoneConfiguration configuration2 =
@@ -41,16 +42,15 @@ public class TimeZoneConfigurationTest {
@Test
public void testIsComplete() {
TimeZoneConfiguration incompleteConfiguration =
new TimeZoneConfiguration.Builder()
.build();
assertFalse(incompleteConfiguration.isComplete());
TimeZoneConfiguration.Builder builder =
new TimeZoneConfiguration.Builder();
assertFalse(builder.build().isComplete());
TimeZoneConfiguration completeConfiguration =
new TimeZoneConfiguration.Builder()
.setAutoDetectionEnabled(true)
.build();
assertTrue(completeConfiguration.isComplete());
builder.setAutoDetectionEnabled(true);
assertFalse(builder.build().isComplete());
builder.setGeoDetectionEnabled(true);
assertTrue(builder.build().isComplete());
}
@Test
@@ -122,6 +122,27 @@ public class TimeZoneConfigurationTest {
TimeZoneConfiguration two = builder2.build();
assertEquals(one, two);
}
builder1.setGeoDetectionEnabled(true);
{
TimeZoneConfiguration one = builder1.build();
TimeZoneConfiguration two = builder2.build();
assertNotEquals(one, two);
}
builder2.setGeoDetectionEnabled(false);
{
TimeZoneConfiguration one = builder1.build();
TimeZoneConfiguration two = builder2.build();
assertNotEquals(one, two);
}
builder1.setGeoDetectionEnabled(false);
{
TimeZoneConfiguration one = builder1.build();
TimeZoneConfiguration two = builder2.build();
assertEquals(one, two);
}
}
@Test
@@ -135,5 +156,11 @@ public class TimeZoneConfigurationTest {
builder.setAutoDetectionEnabled(false);
assertRoundTripParcelable(builder.build());
builder.setGeoDetectionEnabled(false);
assertRoundTripParcelable(builder.build());
builder.setGeoDetectionEnabled(true);
assertRoundTripParcelable(builder.build());
}
}

View File

@@ -72,6 +72,10 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat
builder.setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED);
}
// TODO(b/149014708) Replace this with real logic when the settings storage is fully
// implemented.
builder.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_SUPPORTED);
// The ability to make manual time zone suggestions can also be restricted by policy. With
// the current logic above, this could lead to a situation where a device hardware does not
// support auto detection, the device has been forced into "auto" mode by an admin and the
@@ -90,6 +94,7 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat
public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) {
return new TimeZoneConfiguration.Builder()
.setAutoDetectionEnabled(isAutoDetectionEnabled())
.setGeoDetectionEnabled(isGeoDetectionEnabled())
.build();
}
@@ -105,8 +110,11 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat
// detection: if we wrote it down then we'd set the default explicitly. That might influence
// what happens on later releases that do support auto detection on the same hardware.
if (isAutoDetectionSupported()) {
final int value = configuration.isAutoDetectionEnabled() ? 1 : 0;
Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, value);
final int autoEnabledValue = configuration.isAutoDetectionEnabled() ? 1 : 0;
Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, autoEnabledValue);
final boolean geoTzDetectionEnabledValue = configuration.isGeoDetectionEnabled();
// TODO(b/149014708) Write this down to user-scoped settings once implemented.
}
}
@@ -125,6 +133,14 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat
return false;
}
@Override
public boolean isGeoDetectionEnabled() {
// TODO(b/149014708) Read this from user-scoped settings once implemented. The user's
// location toggle will act as an override for this setting, i.e. so that the setting will
// return false if the location toggle is disabled.
return false;
}
@Override
public boolean isDeviceTimeZoneInitialized() {
// timezone.equals("GMT") will be true and only true if the time zone was

View File

@@ -22,6 +22,7 @@ import android.annotation.NonNull;
* The internal (in-process) system server API for the {@link
* com.android.server.timezonedetector.TimeZoneDetectorService}.
*
* <p>The methods on this class can be called from any thread.
* @hide
*/
public interface TimeZoneDetectorInternal extends Dumpable.Container {
@@ -29,7 +30,7 @@ public interface TimeZoneDetectorInternal extends Dumpable.Container {
/**
* 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.
* available, and so on. This method may be implemented asynchronously.
*/
void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion);
}

View File

@@ -58,6 +58,7 @@ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInter
@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
Objects.requireNonNull(timeZoneSuggestion);
// All strategy calls must take place on the mHandler thread.
mHandler.post(
() -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion));
}

View File

@@ -130,6 +130,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
service.handleAutoTimeZoneConfigChanged();
}
});
// TODO(b/149014708) Listen for changes to geolocation time zone detection enabled config.
// This should also include listening to the current user and the current user's location
// toggle since the config is user-scoped and the location toggle overrides the geolocation
// time zone enabled setting.
return service;
}
@@ -280,7 +284,7 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
void handleConfigurationChanged() {
// Note: we could trigger an async time zone detection operation here via a call to
// handleAutoTimeZoneDetectionChanged(), but that is triggered in response to the underlying
// handleAutoTimeZoneConfigChanged(), but that is triggered in response to the underlying
// setting value changing so it is currently unnecessary. If we get to a point where all
// configuration changes are guaranteed to happen in response to an updateConfiguration()
// call, then we can remove that path and call it here instead.
@@ -288,7 +292,6 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
// Configuration has changed, but each user may have a different view of the configuration.
// It's possible that this will cause unnecessary notifications but that shouldn't be a
// problem.
synchronized (mConfigurationListeners) {
final int userCount = mConfigurationListeners.size();
for (int userIndex = 0; userIndex < userCount; userIndex++) {

View File

@@ -30,9 +30,9 @@ import android.util.IndentingPrintWriter;
* <p>The strategy uses suggestions to decide whether to modify the device's time zone setting
* and what to set it to.
*
* <p>Most calls will be handled by a single thread but that is not true for all calls. For example
* {@link #dump(IndentingPrintWriter, String[])}) may be called on a different thread so
* implementations mustvhandle thread safety.
* <p>Most calls will be handled by a single thread, but that is not true for all calls. For example
* {@link #dump(IndentingPrintWriter, String[])}) may be called on a different thread concurrently
* with other operations so implementations must still handle thread safety.
*
* @hide
*/

View File

@@ -23,6 +23,7 @@ import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_S
import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
import static android.app.timezonedetector.TimeZoneConfiguration.PROPERTY_AUTO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneConfiguration.PROPERTY_GEO_DETECTION_ENABLED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -97,6 +98,12 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
*/
boolean isAutoDetectionEnabled();
/**
* Returns whether geolocation can be used for time zone detection when {@link
* #isAutoDetectionEnabled()} returns {@code true}.
*/
boolean isGeoDetectionEnabled();
/**
* Returns true if the device has had an explicit time zone set.
*/
@@ -200,7 +207,15 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
*/
@GuardedBy("this")
private ArrayMapWithHistory<Integer, QualifiedTelephonyTimeZoneSuggestion>
mSuggestionBySlotIndex = new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
mTelephonySuggestionsBySlotIndex =
new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
/**
* The latest geolocation suggestion received.
*/
@GuardedBy("this")
private ReferenceWithHistory<GeolocationTimeZoneSuggestion> mLatestGeoLocationSuggestion =
new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
@GuardedBy("this")
private final List<Dumpable> mDumpables = new ArrayList<>();
@@ -284,17 +299,26 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
private static boolean containsAutoTimeDetectionProperties(
@NonNull TimeZoneConfiguration configuration) {
return configuration.hasProperty(PROPERTY_AUTO_DETECTION_ENABLED);
return configuration.hasProperty(PROPERTY_AUTO_DETECTION_ENABLED)
|| configuration.hasProperty(PROPERTY_GEO_DETECTION_ENABLED);
}
@Override
public synchronized void suggestGeolocationTimeZone(
@NonNull GeolocationTimeZoneSuggestion suggestion) {
Objects.requireNonNull(suggestion);
if (DBG) {
Slog.d(LOG_TAG, "Geolocation suggestion received. newSuggestion=" + suggestion);
}
// TODO Implement this.
throw new UnsupportedOperationException(
"Geo-location time zone detection is not currently implemented");
Objects.requireNonNull(suggestion);
mLatestGeoLocationSuggestion.set(suggestion);
// Now perform auto time zone detection. The new suggestion may be used to modify the time
// zone setting.
if (mCallback.isGeoDetectionEnabled()) {
String reason = "New geolocation time zone suggested. suggestion=" + suggestion;
doAutoTimeZoneDetection(reason);
}
}
@Override
@@ -332,12 +356,14 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
new QualifiedTelephonyTimeZoneSuggestion(suggestion, score);
// Store the suggestion against the correct slotIndex.
mSuggestionBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion);
mTelephonySuggestionsBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion);
// Now perform auto time zone detection. The new suggestion may be used to modify the time
// zone setting.
String reason = "New telephony time suggested. suggestion=" + suggestion;
doAutoTimeZoneDetection(reason);
if (!mCallback.isGeoDetectionEnabled()) {
String reason = "New telephony time zone suggested. suggestion=" + suggestion;
doAutoTimeZoneDetection(reason);
}
}
private static int scoreTelephonySuggestion(@NonNull TelephonyTimeZoneSuggestion suggestion) {
@@ -363,9 +389,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
}
/**
* Finds the best available time zone suggestion from all slotIndexes. If it is high-enough
* quality and automatic time zone detection is enabled then it will be set on the device. The
* outcome can be that this strategy becomes / remains un-opinionated and nothing is set.
* Performs automatic time zone detection.
*/
@GuardedBy("this")
private void doAutoTimeZoneDetection(@NonNull String detectionReason) {
@@ -374,6 +398,62 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
return;
}
// Use the right suggestions based on the current configuration. This check is potentially
// race-prone until this value is set via a call to TimeZoneDetectorStrategy.
if (mCallback.isGeoDetectionEnabled()) {
doGeolocationTimeZoneDetection(detectionReason);
} else {
doTelephonyTimeZoneDetection(detectionReason);
}
}
/**
* 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.
*/
@GuardedBy("this")
private void doGeolocationTimeZoneDetection(@NonNull String detectionReason) {
GeolocationTimeZoneSuggestion latestGeolocationSuggestion =
mLatestGeoLocationSuggestion.get();
if (latestGeolocationSuggestion == null) {
return;
}
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;
}
// GeolocationTimeZoneSuggestion has no measure of quality. We assume all suggestions are
// reliable.
String zoneId;
// Introduce bias towards the device's current zone when there are multiple zone suggested.
String deviceTimeZone = mCallback.getDeviceTimeZone();
if (zoneIds.contains(deviceTimeZone)) {
if (DBG) {
Slog.d(LOG_TAG,
"Geo tz suggestion contains current device time zone. Applying bias.");
}
zoneId = deviceTimeZone;
} else {
zoneId = zoneIds.get(0);
}
setDeviceTimeZoneIfRequired(zoneId, detectionReason);
}
/**
* Detects the time zone using the latest available telephony time zone suggestions.
* Finds the best available time zone suggestion from all slotIndexes. If it is high-enough
* quality and automatic time zone detection is enabled then it will be set on the device. The
* outcome can be that this strategy becomes / remains un-opinionated and nothing is set.
*/
@GuardedBy("this")
private void doTelephonyTimeZoneDetection(@NonNull String detectionReason) {
QualifiedTelephonyTimeZoneSuggestion bestTelephonySuggestion =
findBestTelephonySuggestion();
@@ -468,9 +548,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
// slotIndex and find the best. Note that we deliberately do not look at age: the caller can
// rate-limit so age is not a strong indicator of confidence. Instead, the callers are
// expected to withdraw suggestions they no longer have confidence in.
for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
for (int i = 0; i < mTelephonySuggestionsBySlotIndex.size(); i++) {
QualifiedTelephonyTimeZoneSuggestion candidateSuggestion =
mSuggestionBySlotIndex.valueAt(i);
mTelephonySuggestionsBySlotIndex.valueAt(i);
if (candidateSuggestion == null) {
// Unexpected
continue;
@@ -505,14 +585,10 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
@Override
public synchronized void handleAutoTimeZoneConfigChanged() {
if (DBG) {
Slog.d(LOG_TAG, "handleTimeZoneDetectionChange() called");
}
if (mCallback.isAutoDetectionEnabled()) {
// When the user enabled time zone detection, run the time zone detection and change the
// device time zone if possible.
String reason = "Auto time zone detection setting enabled.";
doAutoTimeZoneDetection(reason);
Slog.d(LOG_TAG, "handleAutoTimeZoneConfigChanged()");
}
doAutoTimeZoneDetection("handleAutoTimeZoneConfigChanged()");
}
@Override
@@ -532,15 +608,21 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
ipw.println("mCallback.isDeviceTimeZoneInitialized()="
+ mCallback.isDeviceTimeZoneInitialized());
ipw.println("mCallback.getDeviceTimeZone()=" + mCallback.getDeviceTimeZone());
ipw.println("mCallback.isGeoDetectionEnabled()=" + mCallback.isGeoDetectionEnabled());
ipw.println("Time zone change log:");
ipw.increaseIndent(); // level 2
mTimeZoneChangesLog.dump(ipw);
ipw.decreaseIndent(); // level 2
ipw.println("Geolocation suggestion history:");
ipw.increaseIndent(); // level 2
mLatestGeoLocationSuggestion.dump(ipw);
ipw.decreaseIndent(); // level 2
ipw.println("Telephony suggestion history:");
ipw.increaseIndent(); // level 2
mSuggestionBySlotIndex.dump(ipw);
mTelephonySuggestionsBySlotIndex.dump(ipw);
ipw.decreaseIndent(); // level 2
ipw.decreaseIndent(); // level 1
@@ -555,7 +637,15 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
@VisibleForTesting
public synchronized QualifiedTelephonyTimeZoneSuggestion getLatestTelephonySuggestion(
int slotIndex) {
return mSuggestionBySlotIndex.get(slotIndex);
return mTelephonySuggestionsBySlotIndex.get(slotIndex);
}
/**
* A method used to inspect strategy state during tests. Not intended for general use.
*/
@VisibleForTesting
public GeolocationTimeZoneSuggestion getLatestGeolocationSuggestion() {
return mLatestGeoLocationSuggestion.get();
}
/**

View File

@@ -27,6 +27,9 @@ import android.app.timezonedetector.TimeZoneCapabilities;
import android.app.timezonedetector.TimeZoneConfiguration;
import android.util.IndentingPrintWriter;
import java.util.ArrayList;
import java.util.List;
class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
private StrategyListener mListener;
@@ -41,6 +44,7 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
private TelephonyTimeZoneSuggestion mLastTelephonySuggestion;
private boolean mHandleAutoTimeZoneConfigChangedCalled;
private boolean mDumpCalled;
private final List<Dumpable> mDumpables = new ArrayList<>();
@Override
public void setStrategyListener(@NonNull StrategyListener listener) {
@@ -105,7 +109,7 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
@Override
public void addDumpable(Dumpable dumpable) {
// Stubbed
mDumpables.add(dumpable);
}
@Override
@@ -149,4 +153,8 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
void verifyDumpCalled() {
assertTrue(mDumpCalled);
}
void verifyHasDumpable(Dumpable expected) {
assertTrue(mDumpables.contains(expected));
}
}

View File

@@ -75,6 +75,16 @@ public class TimeZoneDetectorInternalImplTest {
mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion);
}
@Test
public void testAddDumpable() throws Exception {
Dumpable stubbedDumpable = mock(Dumpable.class);
mTimeZoneDetectorInternal.addDumpable(stubbedDumpable);
mTestHandler.assertTotalMessagesEnqueued(0);
mFakeTimeZoneDetectorStrategy.verifyHasDumpable(stubbedDumpable);
}
private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() {
return new GeolocationTimeZoneSuggestion(ARBITRARY_ZONE_IDS);
}

View File

@@ -52,6 +52,7 @@ import org.junit.runner.RunWith;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
public class TimeZoneDetectorServiceTest {
@@ -252,6 +253,39 @@ public class TimeZoneDetectorServiceTest {
}
}
@Test(expected = SecurityException.class)
public void testSuggestGeolocationTimeZone_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion();
try {
mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion);
fail();
} finally {
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.SET_TIME_ZONE),
anyString());
}
}
@Test
public void testSuggestGeolocationTimeZone() throws Exception {
doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion();
mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion);
mTestHandler.assertTotalMessagesEnqueued(1);
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.SET_TIME_ZONE),
anyString());
mTestHandler.waitForMessagesToBeProcessed();
mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion);
}
@Test(expected = SecurityException.class)
public void testSuggestManualTimeZone_withoutPermission() {
doThrow(new SecurityException("Mock"))
@@ -346,7 +380,7 @@ public class TimeZoneDetectorServiceTest {
}
@Test
public void testAutoTimeZoneDetectionChanged() throws Exception {
public void testHandleAutoTimeZoneConfigChanged() throws Exception {
mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged();
mTestHandler.assertTotalMessagesEnqueued(1);
mTestHandler.waitForMessagesToBeProcessed();
@@ -370,10 +404,15 @@ public class TimeZoneDetectorServiceTest {
private static TimeZoneCapabilities createTimeZoneCapabilities() {
return new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
.setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
.setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
.setSuggestManualTimeZone(CAPABILITY_POSSESSED)
.build();
}
private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() {
return new GeolocationTimeZoneSuggestion(Arrays.asList("TestZoneId"));
}
private static ManualTimeZoneSuggestion createManualTimeZoneSuggestion() {
return new ManualTimeZoneSuggestion("TestZoneId");
}

View File

@@ -41,6 +41,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
@@ -56,10 +57,10 @@ import org.junit.Before;
import org.junit.Test;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -93,16 +94,26 @@ public class TimeZoneDetectorStrategyImplTest {
TELEPHONY_SCORE_HIGHEST),
};
private static final TimeZoneConfiguration CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED =
private static final TimeZoneConfiguration CONFIG_AUTO_ENABLED =
new TimeZoneConfiguration.Builder()
.setAutoDetectionEnabled(true)
.build();
private static final TimeZoneConfiguration CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED =
private static final TimeZoneConfiguration CONFIG_AUTO_DISABLED =
new TimeZoneConfiguration.Builder()
.setAutoDetectionEnabled(false)
.build();
private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_DISABLED =
new TimeZoneConfiguration.Builder()
.setGeoDetectionEnabled(false)
.build();
private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_ENABLED =
new TimeZoneConfiguration.Builder()
.setGeoDetectionEnabled(true)
.build();
private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
private FakeCallback mFakeCallback;
private MockStrategyListener mMockStrategyListener;
@@ -120,7 +131,7 @@ public class TimeZoneDetectorStrategyImplTest {
public void testGetCapabilities() {
new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
TimeZoneCapabilities expectedCapabilities = mFakeCallback.getCapabilities(USER_ID);
assertEquals(expectedCapabilities, mTimeZoneDetectorStrategy.getCapabilities(USER_ID));
}
@@ -129,7 +140,7 @@ public class TimeZoneDetectorStrategyImplTest {
public void testGetConfiguration() {
new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
TimeZoneConfiguration expectedConfiguration = mFakeCallback.getConfiguration(USER_ID);
assertTrue(expectedConfiguration.isComplete());
assertEquals(expectedConfiguration, mTimeZoneDetectorStrategy.getConfiguration(USER_ID));
@@ -140,20 +151,22 @@ public class TimeZoneDetectorStrategyImplTest {
Script script = new Script();
script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
{
// Check the fake test infra is doing what is expected.
TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled());
assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled());
assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSuggestManualTimeZone());
}
script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED));
{
// Check the fake test infra is doing what is expected.
TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled());
assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled());
assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
}
}
@@ -163,20 +176,22 @@ public class TimeZoneDetectorStrategyImplTest {
Script script = new Script();
script.initializeUser(USER_ID, UserCase.RESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
{
// Check the fake test infra is doing what is expected.
TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled());
assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled());
assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone());
}
script.initializeUser(USER_ID, UserCase.RESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED));
{
// Check the fake test infra is doing what is expected.
TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled());
assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled());
assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone());
}
}
@@ -186,20 +201,22 @@ public class TimeZoneDetectorStrategyImplTest {
Script script = new Script();
script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
{
// Check the fake test infra is doing what is expected.
TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled());
assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled());
assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
}
script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED));
{
// Check the fake test infra is doing what is expected.
TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled());
assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled());
assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
}
}
@@ -208,42 +225,61 @@ public class TimeZoneDetectorStrategyImplTest {
public void testUpdateConfiguration_unrestricted() {
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
// Set the configuration with auto detection enabled.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */);
// Nothing should have happened: it was initialized in this state.
script.verifyConfigurationNotChanged();
// Update the configuration with auto detection disabled.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
script.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */);
// The settings should have been changed and the StrategyListener onChange() called.
script.verifyConfigurationChangedAndReset(
USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
script.verifyConfigurationChangedAndReset(USER_ID,
CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED));
// Update the configuration with auto detection enabled.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */);
// The settings should have been changed and the StrategyListener onChange() called.
script.verifyConfigurationChangedAndReset(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
script.verifyConfigurationChangedAndReset(USER_ID,
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
// Update the configuration to enable geolocation time zone detection.
script.simulateUpdateConfiguration(
USER_ID, CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */);
// The settings should have been changed and the StrategyListener onChange() called.
script.verifyConfigurationChangedAndReset(USER_ID,
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED));
}
@Test
public void testUpdateConfiguration_restricted() {
Script script = new Script()
.initializeUser(USER_ID, UserCase.RESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
// Try to update the configuration with auto detection disabled.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
script.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_DISABLED, false /* expectedResult */);
// The settings should not have been changed: user shouldn't have the capabilities.
script.verifyConfigurationNotChanged();
// Update the configuration with auto detection enabled.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
script.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */);
// The settings should not have been changed: user shouldn't have the capabilities.
script.verifyConfigurationNotChanged();
// Update the configuration to enable geolocation time zone detection.
script.simulateUpdateConfiguration(
USER_ID, CONFIG_GEO_DETECTION_ENABLED, false /* expectedResult */);
// The settings should not have been changed: user shouldn't have the capabilities.
script.verifyConfigurationNotChanged();
@@ -253,16 +289,18 @@ public class TimeZoneDetectorStrategyImplTest {
public void testUpdateConfiguration_autoDetectNotSupported() {
Script script = new Script()
.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
// Try to update the configuration with auto detection disabled.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
script.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_DISABLED, false /* expectedResult */);
// The settings should not have been changed: user shouldn't have the capabilities.
script.verifyConfigurationNotChanged();
// Update the configuration with auto detection enabled.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
script.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */);
// The settings should not have been changed: user shouldn't have the capabilities.
script.verifyConfigurationNotChanged();
@@ -276,7 +314,7 @@ public class TimeZoneDetectorStrategyImplTest {
createEmptySlotIndex2Suggestion();
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
script.simulateTelephonyTimeZoneSuggestion(slotIndex1TimeZoneSuggestion)
@@ -308,6 +346,10 @@ public class TimeZoneDetectorStrategyImplTest {
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
}
/**
* Telephony suggestions have quality metadata. Ordinarily, low scoring suggestions are not
* used, but this is not true if the device's time zone setting is uninitialized.
*/
@Test
public void testTelephonySuggestionsWhenTimeZoneUninitialized() {
assertTrue(TELEPHONY_SCORE_LOW < TELEPHONY_SCORE_USAGE_THRESHOLD);
@@ -319,7 +361,7 @@ public class TimeZoneDetectorStrategyImplTest {
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
// A low quality suggestions will not be taken: The device time zone setting is left
// uninitialized.
@@ -376,16 +418,16 @@ public class TimeZoneDetectorStrategyImplTest {
/**
* Confirms that toggling the auto time zone detection setting has the expected behavior when
* the strategy is "opinionated".
* the strategy is "opinionated" when using telephony auto detection.
*/
@Test
public void testTogglingAutoTimeZoneDetection() {
public void testTogglingAutoDetection_autoTelephony() {
Script script = new Script();
for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) {
// Start with the device in a known state.
script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
TelephonyTimeZoneSuggestion suggestion =
@@ -405,7 +447,8 @@ public class TimeZoneDetectorStrategyImplTest {
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
// Toggling the time zone setting on should cause the device setting to be set.
script.simulateAutoTimeZoneDetectionEnabled(USER_ID, true);
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED,
true /* expectedResult */);
// When time zone detection is already enabled the suggestion (if it scores highly
// enough) should be set immediately.
@@ -422,7 +465,8 @@ public class TimeZoneDetectorStrategyImplTest {
mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
// Toggling the time zone setting should off should do nothing.
script.simulateAutoTimeZoneDetectionEnabled(USER_ID, false)
script.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
.verifyTimeZoneNotChanged();
// Assert internal service state.
@@ -437,7 +481,7 @@ public class TimeZoneDetectorStrategyImplTest {
public void testTelephonySuggestionsSingleSlotId() {
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) {
@@ -451,8 +495,7 @@ public class TimeZoneDetectorStrategyImplTest {
*/
// Each test case will have the same or lower score than the last.
ArrayList<TelephonyTestCase> descendingCasesByScore =
new ArrayList<>(Arrays.asList(TELEPHONY_TEST_CASES));
List<TelephonyTestCase> descendingCasesByScore = list(TELEPHONY_TEST_CASES);
Collections.reverse(descendingCasesByScore);
for (TelephonyTestCase testCase : descendingCasesByScore) {
@@ -504,7 +547,7 @@ public class TimeZoneDetectorStrategyImplTest {
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
// Initialize the latest suggestions as empty so we don't need to worry about nulls
// below for the first loop.
@@ -583,15 +626,15 @@ public class TimeZoneDetectorStrategyImplTest {
}
/**
* The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing
* the time zone is actually necessary. This test proves that the service doesn't assume it
* knows the current setting.
* The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing the time
* zone is actually necessary. This test proves that the strategy doesn't assume it knows the
* current settings.
*/
@Test
public void testTelephonySuggestionTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting() {
public void testTelephonySuggestionStrategyDoesNotAssumeCurrentSetting_autoTelephony() {
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
TelephonyTestCase testCase = newTelephonyTestCase(
MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH);
@@ -609,26 +652,40 @@ public class TimeZoneDetectorStrategyImplTest {
// Toggling time zone detection should set the device time zone only if the current setting
// value is different from the most recent telephony suggestion.
script.simulateAutoTimeZoneDetectionEnabled(USER_ID, false)
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
.verifyTimeZoneNotChanged()
.simulateAutoTimeZoneDetectionEnabled(USER_ID, true)
.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
.verifyTimeZoneNotChanged();
// Simulate a user turning auto detection off, a new suggestion being made while auto
// detection is off, and the user turning it on again.
script.simulateAutoTimeZoneDetectionEnabled(USER_ID, false)
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
.simulateTelephonyTimeZoneSuggestion(newYorkSuggestion)
.verifyTimeZoneNotChanged();
// Latest suggestion should be used.
script.simulateAutoTimeZoneDetectionEnabled(USER_ID, true)
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
.verifyTimeZoneChangedAndReset(newYorkSuggestion);
}
@Test
public void testManualSuggestion_unrestricted_simulateAutoTimeZoneEnabled() {
public void testManualSuggestion_autoDetectionEnabled_autoTelephony() {
checkManualSuggestion_autoDetectionEnabled(false /* geoDetectionEnabled */);
}
@Test
public void testManualSuggestion_autoDetectionEnabled_autoGeo() {
checkManualSuggestion_autoDetectionEnabled(true /* geoDetectionEnabled */);
}
private void checkManualSuggestion_autoDetectionEnabled(boolean geoDetectionEnabled) {
TimeZoneConfiguration geoTzEnabledConfig =
new TimeZoneConfiguration.Builder()
.setGeoDetectionEnabled(geoDetectionEnabled)
.build();
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
CONFIG_AUTO_ENABLED.with(geoTzEnabledConfig))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
// Auto time zone detection is enabled so the manual suggestion should be ignored.
@@ -641,7 +698,7 @@ public class TimeZoneDetectorStrategyImplTest {
public void testManualSuggestion_restricted_simulateAutoTimeZoneEnabled() {
Script script = new Script()
.initializeUser(USER_ID, UserCase.RESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
// Auto time zone detection is enabled so the manual suggestion should be ignored.
@@ -654,7 +711,7 @@ public class TimeZoneDetectorStrategyImplTest {
public void testManualSuggestion_autoDetectNotSupported_simulateAutoTimeZoneEnabled() {
Script script = new Script()
.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
// Auto time zone detection is enabled so the manual suggestion should be ignored.
@@ -668,7 +725,7 @@ public class TimeZoneDetectorStrategyImplTest {
public void testManualSuggestion_unrestricted_autoTimeZoneDetectionDisabled() {
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
// Auto time zone detection is disabled so the manual suggestion should be used.
@@ -682,7 +739,7 @@ public class TimeZoneDetectorStrategyImplTest {
public void testManualSuggestion_restricted_autoTimeZoneDetectionDisabled() {
Script script = new Script()
.initializeUser(USER_ID, UserCase.RESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
// Restricted users do not have the capability.
@@ -696,7 +753,7 @@ public class TimeZoneDetectorStrategyImplTest {
public void testManualSuggestion_autoDetectNotSupported_autoTimeZoneDetectionDisabled() {
Script script = new Script()
.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
// Unrestricted users have the capability.
@@ -706,11 +763,262 @@ public class TimeZoneDetectorStrategyImplTest {
.verifyTimeZoneChangedAndReset(manualSuggestion);
}
@Test
public void testGeoSuggestion_uncertain() {
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
GeolocationTimeZoneSuggestion uncertainSuggestion = createUncertainGeoLocationSuggestion();
script.simulateGeolocationTimeZoneSuggestion(uncertainSuggestion)
.verifyTimeZoneNotChanged();
// Assert internal service state.
assertEquals(uncertainSuggestion,
mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
}
@Test
public void testGeoSuggestion_noZones() {
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
GeolocationTimeZoneSuggestion noZonesSuggestion = createGeoLocationSuggestion(list());
script.simulateGeolocationTimeZoneSuggestion(noZonesSuggestion)
.verifyTimeZoneNotChanged();
// Assert internal service state.
assertEquals(noZonesSuggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
}
@Test
public void testGeoSuggestion_oneZone() {
GeolocationTimeZoneSuggestion suggestion =
createGeoLocationSuggestion(list("Europe/London"));
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
script.simulateGeolocationTimeZoneSuggestion(suggestion)
.verifyTimeZoneChangedAndReset("Europe/London");
// Assert internal service state.
assertEquals(suggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
}
/**
* In the current implementation, the first zone ID is always used unless the device is set to
* one of the other options. This is "stickiness" - the device favors the zone it is currently
* set to until that unambiguously can't be correct.
*/
@Test
public void testGeoSuggestion_multiZone() {
GeolocationTimeZoneSuggestion londonOnlySuggestion =
createGeoLocationSuggestion(list("Europe/London"));
GeolocationTimeZoneSuggestion londonOrParisSuggestion =
createGeoLocationSuggestion(list("Europe/Paris", "Europe/London"));
GeolocationTimeZoneSuggestion parisOnlySuggestion =
createGeoLocationSuggestion(list("Europe/Paris"));
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
script.simulateGeolocationTimeZoneSuggestion(londonOnlySuggestion)
.verifyTimeZoneChangedAndReset("Europe/London");
assertEquals(londonOnlySuggestion,
mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
// Confirm bias towards the current device zone when there's multiple zones to choose from.
script.simulateGeolocationTimeZoneSuggestion(londonOrParisSuggestion)
.verifyTimeZoneNotChanged();
assertEquals(londonOrParisSuggestion,
mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
script.simulateGeolocationTimeZoneSuggestion(parisOnlySuggestion)
.verifyTimeZoneChangedAndReset("Europe/Paris");
assertEquals(parisOnlySuggestion,
mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
// 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());
}
/**
* Confirms that toggling the auto time zone detection enabled setting has the expected behavior
* when the strategy is "opinionated" and "un-opinionated" when in geolocation detection is
* enabled.
*/
@Test
public void testTogglingAutoDetectionEnabled_autoGeo() {
GeolocationTimeZoneSuggestion geolocationSuggestion =
createGeoLocationSuggestion(list("Europe/London"));
GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
createUncertainGeoLocationSuggestion();
ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_ENABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion);
// When time zone detection is not enabled, the time zone suggestion will not be set.
script.verifyTimeZoneNotChanged();
// Assert internal service state.
assertEquals(geolocationSuggestion,
mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
// Toggling the time zone setting on should cause the device setting to be set.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
.verifyTimeZoneChangedAndReset("Europe/London");
// Toggling the time zone setting should off should do nothing because the device is now
// set to that time zone.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
.verifyTimeZoneNotChanged()
.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
.verifyTimeZoneNotChanged();
// Now toggle auto time zone setting, and confirm it is opinionated.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
.simulateManualTimeZoneSuggestion(
USER_ID, manualSuggestion, true /* expectedResult */)
.verifyTimeZoneChangedAndReset(manualSuggestion)
.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
.verifyTimeZoneChangedAndReset("Europe/London");
// Now withdraw the geolocation suggestion, and assert the strategy is no longer
// opinionated.
/* expectedResult */
script.simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
.verifyTimeZoneNotChanged()
.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
.verifyTimeZoneNotChanged()
.simulateManualTimeZoneSuggestion(
USER_ID, manualSuggestion, true /* expectedResult */)
.verifyTimeZoneChangedAndReset(manualSuggestion)
.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
.verifyTimeZoneNotChanged();
// Assert internal service state.
assertEquals(uncertainGeolocationSuggestion,
mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
}
/**
* Confirms that changing the geolocation time zone detection enabled setting has the expected
* behavior, i.e. immediately recompute the detected time zone using different signals.
*/
@Test
public void testChangingGeoDetectionEnabled() {
GeolocationTimeZoneSuggestion geolocationSuggestion =
createGeoLocationSuggestion(list("Europe/London"));
TelephonyTimeZoneSuggestion telephonySuggestion = createTelephonySuggestion(
SLOT_INDEX1, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
"Europe/Paris");
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
// Add suggestions. Nothing should happen as time zone detection is disabled.
script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
.verifyTimeZoneNotChanged();
script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
.verifyTimeZoneNotChanged();
// Assert internal service state.
assertEquals(geolocationSuggestion,
mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
assertEquals(telephonySuggestion,
mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1).suggestion);
// 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
// disabled.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
.verifyTimeZoneChangedAndReset(telephonySuggestion);
// Changing the detection to enable geo detection should cause the device tz setting to
// change to the geo suggestion.
script.simulateUpdateConfiguration(
USER_ID, CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */)
.verifyTimeZoneChangedAndReset(geolocationSuggestion.getZoneIds().get(0));
// Changing the detection to disable geo detection should cause the device tz setting to
// change to the telephony suggestion.
script.simulateUpdateConfiguration(
USER_ID, CONFIG_GEO_DETECTION_DISABLED, true /* expectedResult */)
.verifyTimeZoneChangedAndReset(telephonySuggestion);
}
/**
* The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing the time
* zone is actually necessary. This test proves that the strategy doesn't assume it knows the
* current setting.
*/
@Test
public void testTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting_autoGeo() {
GeolocationTimeZoneSuggestion losAngelesSuggestion =
createGeoLocationSuggestion(list("America/Los_Angeles"));
GeolocationTimeZoneSuggestion newYorkSuggestion =
createGeoLocationSuggestion(list("America/New_York"));
Script script = new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED));
// Initialization.
script.simulateGeolocationTimeZoneSuggestion(losAngelesSuggestion)
.verifyTimeZoneChangedAndReset("America/Los_Angeles");
// Suggest it again - it should not be set because it is already set.
script.simulateGeolocationTimeZoneSuggestion(losAngelesSuggestion)
.verifyTimeZoneNotChanged();
// Toggling time zone detection should set the device time zone only if the current setting
// value is different from the most recent telephony suggestion.
/* expectedResult */
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
.verifyTimeZoneNotChanged()
.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
.verifyTimeZoneNotChanged();
// Simulate a user turning auto detection off, a new suggestion being made while auto
// detection is off, and the user turning it on again.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
.simulateGeolocationTimeZoneSuggestion(newYorkSuggestion)
.verifyTimeZoneNotChanged();
// Latest suggestion should be used.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
.verifyTimeZoneChangedAndReset("America/New_York");
}
@Test
public void testAddDumpable() {
new Script()
.initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
AtomicBoolean dumpCalled = new AtomicBoolean(false);
@@ -733,6 +1041,15 @@ public class TimeZoneDetectorStrategyImplTest {
return new ManualTimeZoneSuggestion(zoneId);
}
private static TelephonyTimeZoneSuggestion createTelephonySuggestion(
int slotIndex, @MatchType int matchType, @Quality int quality, String zoneId) {
return new TelephonyTimeZoneSuggestion.Builder(slotIndex)
.setMatchType(matchType)
.setQuality(quality)
.setZoneId(zoneId)
.build();
}
private static TelephonyTimeZoneSuggestion createEmptySlotIndex1Suggestion() {
return new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX1).build();
}
@@ -741,6 +1058,17 @@ public class TimeZoneDetectorStrategyImplTest {
return new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX2).build();
}
private static GeolocationTimeZoneSuggestion createUncertainGeoLocationSuggestion() {
return createGeoLocationSuggestion(null);
}
private static GeolocationTimeZoneSuggestion createGeoLocationSuggestion(
@Nullable List<String> zoneIds) {
GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(zoneIds);
suggestion.addDebugInfo("Test suggestion");
return suggestion;
}
static class FakeCallback implements TimeZoneDetectorStrategyImpl.Callback {
private TimeZoneCapabilities mCapabilities;
@@ -757,7 +1085,8 @@ public class TimeZoneDetectorStrategyImplTest {
TimeZoneConfiguration configuration) {
assertEquals(userId, capabilities.getUserId());
mCapabilities = capabilities;
assertTrue(configuration.isComplete());
assertTrue("Configuration must be complete when initializing, config=" + configuration,
configuration.isComplete());
mConfiguration.init(new UserConfiguration(userId, configuration));
}
@@ -790,11 +1119,8 @@ public class TimeZoneDetectorStrategyImplTest {
mConfiguration.set(new UserConfiguration(userId, newConfig));
if (!newConfig.equals(oldConfig)) {
if (oldConfig.isAutoDetectionEnabled() != newConfig.isAutoDetectionEnabled()) {
// Simulate what happens when the auto detection enabled configuration is
// changed.
mStrategy.handleAutoTimeZoneConfigChanged();
}
// Simulate what happens when the auto detection configuration is changed.
mStrategy.handleAutoTimeZoneConfigChanged();
}
}
@@ -803,6 +1129,11 @@ public class TimeZoneDetectorStrategyImplTest {
return mConfiguration.getLatest().configuration.isAutoDetectionEnabled();
}
@Override
public boolean isGeoDetectionEnabled() {
return mConfiguration.getLatest().configuration.isGeoDetectionEnabled();
}
@Override
public boolean isDeviceTimeZoneInitialized() {
return mTimeZoneId.getLatest() != null;
@@ -942,32 +1273,33 @@ public class TimeZoneDetectorStrategyImplTest {
* supplied configuration.
*/
private static TimeZoneCapabilities createCapabilities(
int userId, UserCase userRole, TimeZoneConfiguration configuration) {
switch (userRole) {
int userId, UserCase userCase, TimeZoneConfiguration configuration) {
switch (userCase) {
case UNRESTRICTED: {
int suggestManualTimeZoneCapability = configuration.isAutoDetectionEnabled()
? CAPABILITY_NOT_APPLICABLE : CAPABILITY_POSSESSED;
return new TimeZoneCapabilities.Builder(userId)
.setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
.setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
.setSuggestManualTimeZone(suggestManualTimeZoneCapability)
.build();
}
case RESTRICTED: {
return new TimeZoneCapabilities.Builder(userId)
.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED)
.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED)
.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED)
.build();
}
case AUTO_DETECT_NOT_SUPPORTED: {
return new TimeZoneCapabilities.Builder(userId)
.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_SUPPORTED)
.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_SUPPORTED)
.setSuggestManualTimeZone(CAPABILITY_POSSESSED)
.build();
}
default:
throw new AssertionError(userRole + " not recognized");
throw new AssertionError(userCase + " not recognized");
}
}
@@ -978,8 +1310,8 @@ public class TimeZoneDetectorStrategyImplTest {
private class Script {
Script initializeUser(
@UserIdInt int userId, UserCase userRole, TimeZoneConfiguration configuration) {
TimeZoneCapabilities capabilities = createCapabilities(userId, userRole, configuration);
@UserIdInt int userId, UserCase userCase, TimeZoneConfiguration configuration) {
TimeZoneCapabilities capabilities = createCapabilities(userId, userCase, configuration);
mFakeCallback.initializeUser(userId, capabilities, configuration);
return this;
}
@@ -989,27 +1321,24 @@ public class TimeZoneDetectorStrategyImplTest {
return this;
}
Script simulateAutoTimeZoneDetectionEnabled(@UserIdInt int userId, boolean enabled) {
TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder()
.setAutoDetectionEnabled(enabled)
.build();
return simulateUpdateConfiguration(userId, configuration);
}
/**
* Simulates the time zone detection strategy receiving an updated configuration.
* Simulates the time zone detection strategy receiving an updated configuration and checks
* the return value.
*/
Script simulateUpdateConfiguration(
@UserIdInt int userId, TimeZoneConfiguration configuration) {
mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration);
@UserIdInt int userId, TimeZoneConfiguration configuration,
boolean expectedResult) {
assertEquals(expectedResult,
mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration));
return this;
}
/**
* Simulates the time zone detection strategy receiving a telephony-originated suggestion.
* Simulates the time zone detection strategy receiving a geolocation-originated
* suggestion.
*/
Script simulateTelephonyTimeZoneSuggestion(TelephonyTimeZoneSuggestion timeZoneSuggestion) {
mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion);
Script simulateGeolocationTimeZoneSuggestion(GeolocationTimeZoneSuggestion suggestion) {
mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(suggestion);
return this;
}
@@ -1024,13 +1353,26 @@ public class TimeZoneDetectorStrategyImplTest {
return this;
}
/**
* Simulates the time zone detection strategy receiving a telephony-originated suggestion.
*/
Script simulateTelephonyTimeZoneSuggestion(TelephonyTimeZoneSuggestion timeZoneSuggestion) {
mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion);
return this;
}
/**
* Confirms that the device's time zone has not been set by previous actions since the test
* state was last reset.
*/
Script verifyTimeZoneNotChanged() {
mFakeCallback.assertTimeZoneNotChanged();
return this;
}
Script verifyTimeZoneChangedAndReset(TelephonyTimeZoneSuggestion suggestion) {
mFakeCallback.assertTimeZoneChangedTo(suggestion.getZoneId());
/** Verifies the device's time zone has been set and clears change tracking history. */
Script verifyTimeZoneChangedAndReset(String zoneId) {
mFakeCallback.assertTimeZoneChangedTo(zoneId);
mFakeCallback.commitAllChanges();
return this;
}
@@ -1041,6 +1383,12 @@ public class TimeZoneDetectorStrategyImplTest {
return this;
}
Script verifyTimeZoneChangedAndReset(TelephonyTimeZoneSuggestion suggestion) {
mFakeCallback.assertTimeZoneChangedTo(suggestion.getZoneId());
mFakeCallback.commitAllChanges();
return this;
}
/**
* Verifies that the configuration has been changed to the expected value.
*/
@@ -1120,4 +1468,8 @@ public class TimeZoneDetectorStrategyImplTest {
mOnConfigurationChangedCalled = false;
}
}
private static <T> List<T> list(T... values) {
return Arrays.asList(values);
}
}