Merge "Enable telephony fallback triggered by providers"

This commit is contained in:
TreeHugger Robot
2022-11-29 23:01:17 +00:00
committed by Android (Google) Code Review
10 changed files with 462 additions and 91 deletions

View File

@@ -19,6 +19,7 @@ package android.app.time;
import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_RUNNING;
import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED;
import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_UNKNOWN;
import static android.app.time.DetectorStatusTypes.detectionAlgorithmStatusFromString;
import static android.app.time.DetectorStatusTypes.detectionAlgorithmStatusToString;
import static android.app.time.DetectorStatusTypes.requireValidDetectionAlgorithmStatus;
@@ -319,6 +320,40 @@ public final class LocationTimeZoneAlgorithmStatus implements Parcelable {
mSecondaryProviderStatus, mSecondaryProviderReportedStatus);
}
/**
* Returns {@code true} if the algorithm status could allow the time zone detector to enter
* telephony fallback mode.
*/
public boolean couldEnableTelephonyFallback() {
if (mStatus == DETECTION_ALGORITHM_STATUS_UNKNOWN
|| mStatus == DETECTION_ALGORITHM_STATUS_NOT_RUNNING
|| mStatus == DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED) {
// This method is not expected to be called on objects with these statuses. Fallback
// should not be enabled if it is.
return false;
}
// mStatus == DETECTOR_STATUS_RUNNING.
boolean primarySuggestsFallback = false;
if (mPrimaryProviderStatus == PROVIDER_STATUS_NOT_PRESENT) {
primarySuggestsFallback = true;
} else if (mPrimaryProviderStatus == PROVIDER_STATUS_IS_UNCERTAIN
&& mPrimaryProviderReportedStatus != null) {
primarySuggestsFallback = mPrimaryProviderReportedStatus.couldEnableTelephonyFallback();
}
boolean secondarySuggestsFallback = false;
if (mSecondaryProviderStatus == PROVIDER_STATUS_NOT_PRESENT) {
secondarySuggestsFallback = true;
} else if (mSecondaryProviderStatus == PROVIDER_STATUS_IS_UNCERTAIN
&& mSecondaryProviderReportedStatus != null) {
secondarySuggestsFallback =
mSecondaryProviderReportedStatus.couldEnableTelephonyFallback();
}
return primarySuggestsFallback && secondarySuggestsFallback;
}
/** @hide */
@VisibleForTesting
@NonNull

View File

@@ -47,6 +47,7 @@ android_test {
"androidx.test.ext.junit",
"androidx.test.runner",
"androidx.test.rules",
"junit-params",
"kotlin-test",
"mockito-target-minus-junit4",
"ub-uiautomator",

View File

@@ -17,19 +17,26 @@
package android.app.time;
import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_RUNNING;
import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED;
import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_UNKNOWN;
import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_CERTAIN;
import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_UNCERTAIN;
import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_PRESENT;
import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_READY;
import static android.app.time.ParcelableTestSupport.assertEqualsAndHashCode;
import static android.app.time.ParcelableTestSupport.assertRoundTripParcelable;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_NOT_APPLICABLE;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK;
import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_NOT_APPLICABLE;
import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_OK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import android.app.time.LocationTimeZoneAlgorithmStatus.ProviderStatus;
import android.service.timezone.TimeZoneProviderStatus;
@@ -207,4 +214,114 @@ public class LocationTimeZoneAlgorithmStatusTest {
assertEquals(status,
LocationTimeZoneAlgorithmStatus.parseCommandlineArg(status.toString()));
}
@Test
public void testCouldEnableTelephonyFallback_notRunning() {
LocationTimeZoneAlgorithmStatus notRunning =
new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_NOT_RUNNING,
PROVIDER_STATUS_NOT_READY, null, PROVIDER_STATUS_NOT_READY, null);
assertFalse(notRunning.couldEnableTelephonyFallback());
}
@Test
public void testCouldEnableTelephonyFallback_unknown() {
// DETECTION_ALGORITHM_STATUS_UNKNOWN must never allow fallback
LocationTimeZoneAlgorithmStatus unknown =
new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_UNKNOWN,
PROVIDER_STATUS_NOT_READY, null, PROVIDER_STATUS_NOT_READY, null);
assertFalse(unknown.couldEnableTelephonyFallback());
}
@Test
public void testCouldEnableTelephonyFallback_notSupported() {
// DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED must never allow fallback
LocationTimeZoneAlgorithmStatus notSupported =
new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED,
PROVIDER_STATUS_NOT_READY, null, PROVIDER_STATUS_NOT_READY, null);
assertFalse(notSupported.couldEnableTelephonyFallback());
}
@Test
public void testCouldEnableTelephonyFallback_running() {
// DETECTION_ALGORITHM_STATUS_RUNNING may allow fallback
// Sample provider-reported statuses that do / do not enable fallback.
TimeZoneProviderStatus enableTelephonyFallbackProviderStatus =
new TimeZoneProviderStatus.Builder()
.setLocationDetectionDependencyStatus(
DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT)
.setConnectivityDependencyStatus(DEPENDENCY_STATUS_NOT_APPLICABLE)
.setTimeZoneResolutionOperationStatus(OPERATION_STATUS_NOT_APPLICABLE)
.build();
assertTrue(enableTelephonyFallbackProviderStatus.couldEnableTelephonyFallback());
TimeZoneProviderStatus notEnableTelephonyFallbackProviderStatus =
new TimeZoneProviderStatus.Builder()
.setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_NOT_APPLICABLE)
.setConnectivityDependencyStatus(DEPENDENCY_STATUS_NOT_APPLICABLE)
.setTimeZoneResolutionOperationStatus(OPERATION_STATUS_NOT_APPLICABLE)
.build();
assertFalse(notEnableTelephonyFallbackProviderStatus.couldEnableTelephonyFallback());
// Provider not ready: Never enable fallback
{
LocationTimeZoneAlgorithmStatus status =
new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
PROVIDER_STATUS_NOT_READY, null, PROVIDER_STATUS_NOT_READY, null);
assertFalse(status.couldEnableTelephonyFallback());
}
// Provider uncertain without reported status: Never enable fallback
{
LocationTimeZoneAlgorithmStatus status =
new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
PROVIDER_STATUS_IS_UNCERTAIN, null, PROVIDER_STATUS_NOT_READY, null);
assertFalse(status.couldEnableTelephonyFallback());
}
{
LocationTimeZoneAlgorithmStatus status =
new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
PROVIDER_STATUS_IS_UNCERTAIN, null, PROVIDER_STATUS_NOT_PRESENT, null);
assertFalse(status.couldEnableTelephonyFallback());
}
// Provider uncertain with reported status: Fallback is based on the status for present
// providers that report their status. All present providers must have reported status and
// agree that fallback is a good idea.
{
LocationTimeZoneAlgorithmStatus status =
new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
PROVIDER_STATUS_IS_UNCERTAIN, enableTelephonyFallbackProviderStatus,
PROVIDER_STATUS_NOT_READY, null);
assertFalse(status.couldEnableTelephonyFallback());
}
{
LocationTimeZoneAlgorithmStatus status =
new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
PROVIDER_STATUS_IS_UNCERTAIN, enableTelephonyFallbackProviderStatus,
PROVIDER_STATUS_NOT_PRESENT, null);
assertTrue(status.couldEnableTelephonyFallback());
}
{
LocationTimeZoneAlgorithmStatus status =
new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
PROVIDER_STATUS_IS_UNCERTAIN, enableTelephonyFallbackProviderStatus,
PROVIDER_STATUS_IS_UNCERTAIN, enableTelephonyFallbackProviderStatus);
assertTrue(status.couldEnableTelephonyFallback());
}
{
LocationTimeZoneAlgorithmStatus status =
new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
PROVIDER_STATUS_IS_UNCERTAIN, enableTelephonyFallbackProviderStatus,
PROVIDER_STATUS_IS_UNCERTAIN, notEnableTelephonyFallbackProviderStatus);
assertFalse(status.couldEnableTelephonyFallback());
}
{
LocationTimeZoneAlgorithmStatus status =
new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
PROVIDER_STATUS_NOT_PRESENT, null,
PROVIDER_STATUS_IS_UNCERTAIN, enableTelephonyFallbackProviderStatus);
assertTrue(status.couldEnableTelephonyFallback());
}
}
}

View File

@@ -16,15 +16,31 @@
package android.service.timezone;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_UNKNOWN;
import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_FAILED;
import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_OK;
import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_UNKNOWN;
import static org.junit.Assert.assertEquals;
import android.service.timezone.TimeZoneProviderStatus.DependencyStatus;
import android.service.timezone.TimeZoneProviderStatus.OperationStatus;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
/** Non-SDK tests. See CTS for SDK API tests. */
@RunWith(JUnitParamsRunner.class)
public class TimeZoneProviderStatusTest {
@Test
@@ -37,4 +53,51 @@ public class TimeZoneProviderStatusTest {
assertEquals(status, TimeZoneProviderStatus.parseProviderStatus(status.toString()));
}
@Test
@Parameters(method = "couldEnableTelephonyFallbackParams")
public void couldEnableTelephonyFallback(@DependencyStatus int locationDetectionStatus,
@DependencyStatus int connectivityStatus, @OperationStatus int tzResolutionStatus) {
TimeZoneProviderStatus providerStatus =
new TimeZoneProviderStatus.Builder()
.setLocationDetectionDependencyStatus(locationDetectionStatus)
.setConnectivityDependencyStatus(connectivityStatus)
.setTimeZoneResolutionOperationStatus(tzResolutionStatus)
.build();
boolean locationDetectionStatusCouldEnableFallback =
(locationDetectionStatus == DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT
|| locationDetectionStatus == DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS);
boolean connectivityStatusCouldEnableFallback =
(connectivityStatus == DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT
|| connectivityStatus == DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS);
boolean tzResolutionStatusCouldEnableFallback = false;
assertEquals(locationDetectionStatusCouldEnableFallback
|| connectivityStatusCouldEnableFallback
|| tzResolutionStatusCouldEnableFallback,
providerStatus.couldEnableTelephonyFallback());
}
public static Integer[][] couldEnableTelephonyFallbackParams() {
List<Integer[]> params = new ArrayList<>();
@DependencyStatus int[] dependencyStatuses =
IntStream.rangeClosed(
DEPENDENCY_STATUS_UNKNOWN, DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS).toArray();
@OperationStatus int[] operationStatuses =
IntStream.rangeClosed(OPERATION_STATUS_UNKNOWN, OPERATION_STATUS_FAILED).toArray();
// Cartesian product: dependencyStatus x dependencyStatus x operationStatus
for (@DependencyStatus int locationDetectionStatus : dependencyStatuses) {
for (@DependencyStatus int connectivityStatus : dependencyStatuses) {
for (@OperationStatus int tzResolutionStatus : operationStatuses) {
params.add(new Integer[] {
locationDetectionStatus,
connectivityStatus,
tzResolutionStatus
});
}
}
}
return params.toArray(new Integer[0][0]);
}
}

View File

@@ -91,7 +91,7 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
deviceActivityMonitor.addListener(new DeviceActivityMonitor.Listener() {
@Override
public void onFlightComplete() {
timeZoneDetectorStrategy.enableTelephonyTimeZoneFallback();
timeZoneDetectorStrategy.enableTelephonyTimeZoneFallback("onFlightComplete()");
}
});
@@ -402,9 +402,9 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
* 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() {
void enableTelephonyFallback(@NonNull String reason) {
enforceManageTimeZoneDetectorPermission();
mTimeZoneDetectorStrategy.enableTelephonyTimeZoneFallback();
mTimeZoneDetectorStrategy.enableTelephonyTimeZoneFallback(reason);
}
/**

View File

@@ -189,7 +189,7 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
}
private int runEnableTelephonyFallback() {
mInterface.enableTelephonyFallback();
mInterface.enableTelephonyFallback("Command line");
return 0;
}

View File

@@ -35,13 +35,13 @@ import android.util.IndentingPrintWriter;
* <p>Devices can have zero, one or two automatic time zone detection algorithms available at any
* point in time.
*
* <p>The two automatic detection algorithms supported are "telephony" and "geolocation". Algorithm
* <p>The two automatic detection algorithms supported are "telephony" and "location". Algorithm
* availability and use depends on several factors:
* <ul>
* <li>Telephony is only available on devices with a telephony stack.
* <li>Geolocation is also optional and configured at image creation time. When enabled on a
* device, its availability depends on the current user's settings, so switching between users can
* change the automatic algorithm used by the device.</li>
* <li>Location is also optional and configured at image creation time. When enabled on a device,
* its availability depends on the current user's settings, so switching between users can change
* the automatic detection algorithm used by the device.</li>
* </ul>
*
* <p>If there are no automatic time zone detections algorithms available then the user can usually
@@ -56,14 +56,14 @@ import android.util.IndentingPrintWriter;
* slotIndexes must have an empty suggestion submitted in order to "withdraw" their previous
* suggestion otherwise it will remain in use.
*
* <p>Geolocation detection is dependent on the current user and their settings. The device retains
* at most one geolocation suggestion. Generally, use of a device's location is dependent on the
* user's "location toggle", but even when that is enabled the user may choose to enable / disable
* the use of geolocation for device time zone detection. If the current user changes to one that
* does not have geolocation detection enabled, or the user turns off geolocation detection, then
* the strategy discards the latest geolocation suggestion. Devices that lose a location fix must
* have an empty suggestion submitted in order to "withdraw" their previous suggestion otherwise it
* will remain in use.
* <p>Location-based detection is dependent on the current user and their settings. The device
* retains at most one geolocation suggestion. Generally, use of a device's location is dependent on
* the user's "location toggle", but even when that is enabled the user may choose to enable /
* disable the use of location for device time zone detection. If the current user changes to one
* that does not have location-based detection enabled, or the user turns off the location-based
* detection, then the strategy will be sent an event that clears the latest suggestion. Devices
* that lose their location fix must have an empty suggestion submitted in order to "withdraw" their
* previous suggestion otherwise it will remain in use.
*
* <p>The strategy uses only one algorithm at a time and does not attempt consensus even when
* more than one is available on a device. This "use only one" behavior is deliberate as different
@@ -72,25 +72,27 @@ 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>When the location detection algorithm 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 location-based detection algorithm to reach certainty.
* Location-based 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 location
* and telephony algorithms, the strategy provides "telephony fallback mode" behavior, which can be
* set to "supported" via device config. Fallback mode is entered at runtime in response to signals
* from outside of the strategy, e.g. from a call to {@link
* #enableTelephonyTimeZoneFallback(String)}, or from information in the latest {@link
* LocationAlgorithmEvent}. For telephony fallback mode to actually use a telephony suggestion, the
* location algorithm <em>must</em> report it is uncertain. Telephony fallback allows the use of
* telephony suggestions to help with faster detection but only until the location algorithm
* provides a concrete, "certain" suggestion. After the location algorithm has made a certain
* suggestion, telephony fallback mode is disabled.
*
* <p>Threading:
*
* <p>Implementations of this class must be thread-safe as calls calls like {@link
* #generateMetricsState()} and {@link #dump(IndentingPrintWriter, String[])} may be called on
* differents thread concurrently with other operations.
* different threads concurrently with other operations.
*
* @hide
*/
@@ -181,11 +183,11 @@ 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 #handleLocationAlgorithmEvent(LocationAlgorithmEvent)} can
* disable it again. See {@link TimeZoneDetectorStrategy} for details.
* Tells the strategy that it can fall back to telephony detection while the location detection
* algorithm remains uncertain. {@link #handleLocationAlgorithmEvent(LocationAlgorithmEvent)}
* can disable it again. See {@link TimeZoneDetectorStrategy} for details.
*/
void enableTelephonyTimeZoneFallback();
void enableTelephonyTimeZoneFallback(@NonNull String reason);
/** Generates a state snapshot for metrics. */
@NonNull
@@ -194,6 +196,6 @@ public interface TimeZoneDetectorStrategy extends Dumpable {
/** Returns {@code true} if the device supports telephony time zone detection. */
boolean isTelephonyTimeZoneDetectionSupported();
/** Returns {@code true} if the device supports geolocation time zone detection. */
/** Returns {@code true} if the device supports location-based time zone detection. */
boolean isGeoTimeZoneDetectionSupported();
}

View File

@@ -63,12 +63,8 @@ import java.util.Objects;
public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrategy {
/**
* Used by {@link TimeZoneDetectorStrategyImpl} to interact with device configuration / settings
* / system properties. It can be faked for testing.
*
* <p>Note: Because the settings / system properties-derived values can currently be modified
* independently and from different threads (and processes!), their use is prone to race
* conditions.
* Used by {@link TimeZoneDetectorStrategyImpl} to interact with device state besides that
* available from {@link #mServiceConfigAccessor}. It can be faked for testing.
*/
@VisibleForTesting
public interface Environment {
@@ -234,7 +230,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
* 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
* value is maintained even when it isn't supported as support can be turned on at any time via
* server flags. The elapsed realtime when the mode last changed is used to help ordering
* between fallback mode switches and suggestions.
*
@@ -421,10 +417,15 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
notifyStateChangeListenersAsynchronously();
}
// Update the mTelephonyTimeZoneFallbackEnabled state if needed: a certain suggestion
// will usually disable telephony fallback mode if it is currently enabled.
// TODO(b/236624675)Some provider status codes can be used to enable telephony fallback.
disableTelephonyFallbackIfNeeded();
// Manage telephony fallback state.
if (event.getAlgorithmStatus().couldEnableTelephonyFallback()) {
// An event may trigger entry into telephony fallback mode if the status
// indicates the location algorithm cannot work and is likely to stay not working.
enableTelephonyTimeZoneFallback("handleLocationAlgorithmEvent(), event=" + event);
} else {
// A certain suggestion will exit telephony fallback mode.
disableTelephonyFallbackIfNeeded();
}
// Now perform auto time zone detection. The new event may be used to modify the time zone
// setting.
@@ -497,38 +498,41 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
}
@Override
public synchronized void enableTelephonyTimeZoneFallback() {
// Only do any work if fallback is currently not enabled.
public synchronized void enableTelephonyTimeZoneFallback(@NonNull String reason) {
// Only do any work to enter fallback mode if fallback is currently not already enabled.
if (!mTelephonyTimeZoneFallbackEnabled.getValue()) {
ConfigurationInternal currentUserConfig = mCurrentConfigurationInternal;
final boolean fallbackEnabled = true;
mTelephonyTimeZoneFallbackEnabled = new TimestampedValue<>(
mEnvironment.elapsedRealtimeMillis(), fallbackEnabled);
String logMsg = "enableTelephonyTimeZoneFallbackMode: "
+ " currentUserConfig=" + currentUserConfig
+ ", mTelephonyTimeZoneFallbackEnabled="
+ mTelephonyTimeZoneFallbackEnabled;
String logMsg = "enableTelephonyTimeZoneFallback: "
+ " reason=" + reason
+ ", currentUserConfig=" + currentUserConfig
+ ", mTelephonyTimeZoneFallbackEnabled=" + mTelephonyTimeZoneFallbackEnabled;
logTimeZoneDebugInfo(logMsg);
// mTelephonyTimeZoneFallbackEnabled and mLatestLocationAlgorithmEvent interact.
// If the latest event contains a "certain" geolocation suggestion, then the telephony
// fallback value needs to be considered after changing it.
// If the latest location algorithm event contains a "certain" geolocation suggestion,
// then the telephony fallback mode needs to be (re)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.
// future, the following call will usually be a no-op, and telephony fallback mode will
// remain enabled. This comment / call is left as a reminder that it is possible in some
// cases for there to be a current, "certain" geolocation suggestion when an attempt is
// made to enable telephony fallback mode and it is intentional that fallback mode stays
// enabled in this case. The choice to do this is mostly for symmetry WRT the case where
// fallback is enabled and then an old "certain" geolocation suggestion is received;
// that would also leave telephony fallback mode enabled.
//
// This choice means that telephony fallback mode remains enabled if there is an
// existing "certain" suggestion until a new "certain" geolocation suggestion is
// received. If, instead, the next geolocation suggestion is "uncertain", then telephony
// fallback, i.e. the use of a telephony suggestion, will actually occur.
disableTelephonyFallbackIfNeeded();
if (currentUserConfig.isTelephonyFallbackSupported()) {
String reason = "enableTelephonyTimeZoneFallbackMode";
doAutoTimeZoneDetection(currentUserConfig, reason);
}
}

View File

@@ -111,7 +111,7 @@ public class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
}
@Override
public void enableTelephonyTimeZoneFallback() {
public void enableTelephonyTimeZoneFallback(String reason) {
throw new UnsupportedOperationException();
}

View File

@@ -29,6 +29,9 @@ import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYP
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_UNKNOWN;
import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_UNKNOWN;
import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_LOW;
@@ -65,6 +68,7 @@ import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality;
import android.os.HandlerThread;
import android.service.timezone.TimeZoneProviderStatus;
import com.android.server.SystemTimeZone.TimeZoneConfidence;
import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion;
@@ -1148,7 +1152,7 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Test
public void testTelephonyFallback() {
public void testTelephonyFallback_enableTelephonyTimeZoneFallbackCalled() {
ConfigurationInternal config = new ConfigurationInternal.Builder(
CONFIG_AUTO_ENABLED_GEO_ENABLED)
.setTelephonyFallbackSupported(true)
@@ -1178,19 +1182,21 @@ public class TimeZoneDetectorStrategyImplTest {
// Receiving an "uncertain" geolocation suggestion should have no effect.
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(true);
}
// Receiving a "certain" geolocation suggestion should disable telephony fallback mode.
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent locationAlgorithmEvent =
createCertainLocationAlgorithmEvent("Europe/London");
script.simulateIncrementClock()
.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneChangedAndReset(locationAlgorithmEvent)
.verifyTelephonyFallbackIsEnabled(false);
}
@@ -1214,17 +1220,19 @@ public class TimeZoneDetectorStrategyImplTest {
// Geolocation suggestions should continue to be used as normal (previous telephony
// suggestions are not used, even when the geolocation suggestion is uncertain).
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent certainLocationAlgorithmEvent =
createCertainLocationAlgorithmEvent("Europe/Rome");
script.simulateIncrementClock()
.simulateLocationAlgorithmEvent(certainLocationAlgorithmEvent)
script.simulateLocationAlgorithmEvent(certainLocationAlgorithmEvent)
.verifyTimeZoneChangedAndReset(certainLocationAlgorithmEvent)
.verifyTelephonyFallbackIsEnabled(false);
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent uncertainLocationAlgorithmEvent =
createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
.simulateLocationAlgorithmEvent(uncertainLocationAlgorithmEvent)
script.simulateLocationAlgorithmEvent(uncertainLocationAlgorithmEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false);
@@ -1246,19 +1254,21 @@ public class TimeZoneDetectorStrategyImplTest {
// Make the geolocation algorithm uncertain.
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneChangedAndReset(lastTelephonySuggestion)
.verifyTelephonyFallbackIsEnabled(true);
}
// Make the geolocation algorithm certain, disabling telephony fallback.
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent locationAlgorithmEvent =
createCertainLocationAlgorithmEvent("Europe/Lisbon");
script.simulateIncrementClock()
.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneChangedAndReset(locationAlgorithmEvent)
.verifyTelephonyFallbackIsEnabled(false);
@@ -1267,9 +1277,10 @@ public class TimeZoneDetectorStrategyImplTest {
// Demonstrate what happens when geolocation is uncertain when telephony fallback is
// enabled.
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false)
.simulateEnableTelephonyFallback()
@@ -1278,6 +1289,132 @@ public class TimeZoneDetectorStrategyImplTest {
}
}
@Test
public void testTelephonyFallback_locationAlgorithmEventSuggestsFallback() {
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, TIME_ZONE_CONFIDENCE_LOW)
.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 without a status should have no effect.
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent();
script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(true);
}
// Receiving a "certain" geolocation suggestion should disable telephony fallback mode.
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent locationAlgorithmEvent =
createCertainLocationAlgorithmEvent("Europe/London");
script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneChangedAndReset(locationAlgorithmEvent)
.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).
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent certainLocationAlgorithmEvent =
createCertainLocationAlgorithmEvent("Europe/Rome");
script.simulateLocationAlgorithmEvent(certainLocationAlgorithmEvent)
.verifyTimeZoneChangedAndReset(certainLocationAlgorithmEvent)
.verifyTelephonyFallbackIsEnabled(false);
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent uncertainLocationAlgorithmEvent =
createUncertainLocationAlgorithmEvent();
script.simulateLocationAlgorithmEvent(uncertainLocationAlgorithmEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false);
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent certainLocationAlgorithmEvent2 =
createCertainLocationAlgorithmEvent("Europe/Rome");
script.simulateLocationAlgorithmEvent(certainLocationAlgorithmEvent2)
// No change needed, device will already be set to Europe/Rome.
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false);
}
// Enable telephony fallback via a LocationAlgorithmEvent containing an "uncertain"
// suggestion.
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
TimeZoneProviderStatus primaryProviderReportedStatus =
new TimeZoneProviderStatus.Builder()
.setLocationDetectionDependencyStatus(
DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS)
.setConnectivityDependencyStatus(DEPENDENCY_STATUS_UNKNOWN)
.setTimeZoneResolutionOperationStatus(OPERATION_STATUS_UNKNOWN)
.build();
LocationAlgorithmEvent uncertainEventBlockedBySettings =
createUncertainLocationAlgorithmEvent(primaryProviderReportedStatus);
script.simulateLocationAlgorithmEvent(uncertainEventBlockedBySettings)
.verifyTimeZoneChangedAndReset(lastTelephonySuggestion)
.verifyTelephonyFallbackIsEnabled(true);
}
// Make the geolocation algorithm certain, disabling telephony fallback.
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent locationAlgorithmEvent =
createCertainLocationAlgorithmEvent("Europe/Lisbon");
script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneChangedAndReset(locationAlgorithmEvent)
.verifyTelephonyFallbackIsEnabled(false);
}
}
@Test
public void testTelephonyFallback_noTelephonySuggestionToFallBackTo() {
ConfigurationInternal config = new ConfigurationInternal.Builder(
@@ -1297,9 +1434,10 @@ public class TimeZoneDetectorStrategyImplTest {
// Receiving an "uncertain" geolocation suggestion should have no effect.
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(true);
}
@@ -1307,9 +1445,10 @@ public class TimeZoneDetectorStrategyImplTest {
// Make an uncertain geolocation suggestion, there is no telephony suggestion to fall back
// to
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(true);
}
@@ -1319,16 +1458,18 @@ public class TimeZoneDetectorStrategyImplTest {
// Geolocation suggestions should continue to be used as normal (previous telephony
// suggestions are not used, even when the geolocation suggestion is uncertain).
{
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent certainEvent =
createCertainLocationAlgorithmEvent("Europe/Rome");
script.simulateIncrementClock()
.simulateLocationAlgorithmEvent(certainEvent)
script.simulateLocationAlgorithmEvent(certainEvent)
.verifyTimeZoneChangedAndReset(certainEvent)
.verifyTelephonyFallbackIsEnabled(false);
// Increment the clock before creating the event: the clock's value is used by the event
script.simulateIncrementClock();
LocationAlgorithmEvent uncertainEvent = createUncertainLocationAlgorithmEvent();
script.simulateIncrementClock()
.simulateLocationAlgorithmEvent(uncertainEvent)
script.simulateLocationAlgorithmEvent(uncertainEvent)
.verifyTimeZoneNotChanged()
.verifyTelephonyFallbackIsEnabled(false);
@@ -1549,9 +1690,16 @@ public class TimeZoneDetectorStrategyImplTest {
}
private LocationAlgorithmEvent createUncertainLocationAlgorithmEvent() {
TimeZoneProviderStatus primaryProviderReportedStatus = null;
return createUncertainLocationAlgorithmEvent(primaryProviderReportedStatus);
}
private LocationAlgorithmEvent createUncertainLocationAlgorithmEvent(
TimeZoneProviderStatus primaryProviderReportedStatus) {
GeolocationTimeZoneSuggestion suggestion = createUncertainGeolocationSuggestion();
LocationTimeZoneAlgorithmStatus algorithmStatus = new LocationTimeZoneAlgorithmStatus(
DETECTION_ALGORITHM_STATUS_RUNNING, PROVIDER_STATUS_IS_UNCERTAIN, null,
DETECTION_ALGORITHM_STATUS_RUNNING,
PROVIDER_STATUS_IS_UNCERTAIN, primaryProviderReportedStatus,
PROVIDER_STATUS_NOT_PRESENT, null);
LocationAlgorithmEvent event = new LocationAlgorithmEvent(algorithmStatus, suggestion);
event.addDebugInfo("Test uncertain event");
@@ -1744,11 +1892,12 @@ public class TimeZoneDetectorStrategyImplTest {
}
/**
* Simulates the time zone detection strategty receiving a signal that allows it to do
* Simulates the time zone detection strategy receiving a signal that allows it to do
* telephony fallback.
*/
Script simulateEnableTelephonyFallback() {
mTimeZoneDetectorStrategy.enableTelephonyTimeZoneFallback();
mTimeZoneDetectorStrategy.enableTelephonyTimeZoneFallback(
"simulateEnableTelephonyFallback()");
return this;
}