Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7fbcae6e authored by Neil Fuller's avatar Neil Fuller
Browse files

Enable telephony fallback triggered by providers

Now that providers can report their status, we can use this to trigger
telephony fallback mode. This opens the door to more sophisticated
mechanisms for things like flight or tunnel detection. After all,
providers know how they work and what they can / cannot do, and may have
access to sensors and providers that the platform has not.

The new "provider reported" flight mode entry cannot replace the
"time_zone_detector"-level behavior entered via calls to
TimeZoneDetectorStrategy as provider status is new and optional;
providers are not required to report their status so it can be omitted.

Bug: 236624675
Test: atest services/tests/servicestests/src/com/android/server/timezonedetector/
Change-Id: I89fb889ba98b3ddedd271670db09f1bb9df95644
parent c868e22d
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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",
+117 −0
Original line number Diff line number Diff line
@@ -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());
        }
    }
}
+63 −0
Original line number Diff line number Diff line
@@ -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]);
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -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);
    }

    /**
Loading