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

Commit 7662ffbb authored by Neil Fuller's avatar Neil Fuller
Browse files

Clear NITZ state when entering airplane mode

Clear NITZ time / time zone detection state when entering airplane mode.

This is to avoid an issue when users do the following:
1) Get on a flight
2) Set device to airplane mode
3) Turn off auto time zone detection
4) Manually set the device time zone to the destination time zone
5) Turn off airplane mode
6) Turn on auto time zone detection

The device would switch to the time zone of the departure point because it
retained state from before the flight, which caused annoyance for users.

Bug: 133492648
Test: atest com.android.internal.telephony.NitzStateMachineImplTest
Change-Id: I1266a53bf14097c72b9f122725ebbd75c439b05a
parent c47b6490
Loading
Loading
Loading
Loading
+32 −3
Original line number Diff line number Diff line
@@ -305,8 +305,24 @@ public final class NitzStateMachineImpl implements NitzStateMachine {

    @Override
    public void handleAirplaneModeChanged(boolean on) {
        if (DBG) {
            Rlog.d(LOG_TAG, "handleAirplaneModeChanged: on=" + on);
        // TODO
        }

        // Treat entry / exit from airplane mode as a strong signal that the user wants to clear
        // cached state. If the user really is boarding a plane they won't want cached state from
        // before their flight influencing behavior.
        //
        // State is cleared on entry AND exit: on entry because the detection code shouldn't be
        // opinionated while in airplane mode, and on exit to avoid any unexpected signals received
        // while in airplane mode from influencing behavior afterwards.
        //
        // After clearing detection state, the time zone detection should work out from first
        // principles what the time / time zone is. This assumes calls like handleNetworkAvailable()
        // will be made after airplane mode is re-enabled as the device re-establishes network
        // connectivity.
        clearTimeDetectionState();
        clearTimeZoneDetectionState();
    }

    private void updateTimeFromNitz() {
@@ -399,6 +415,11 @@ public final class NitzStateMachineImpl implements NitzStateMachine {
        }
    }

    private void clearTimeDetectionState() {
        mSavedNitzTime = null;
        mTimeZoneLog.log("clearTimeZoneDetectionState: All time detection state cleared.");
    }

    private void setAndBroadcastNetworkSetTimeZone(String zoneId, String logMessage) {
        logMessage += " [Setting device time zone to zoneId=" + zoneId + "]";
        if (DBG) {
@@ -497,6 +518,15 @@ public final class NitzStateMachineImpl implements NitzStateMachine {
        }
    }

    private void clearTimeZoneDetectionState() {
        mLatestNitzSignal = null;
        mGotCountryCode = false;
        mSavedTimeZoneId = null;
        mNitzTimeZoneDetectionSuccessful = false;
        mTimeZoneLog.log("clearTimeZoneDetectionState: All time zone detection state cleared.");
    }

    // VisibleForTesting
    public boolean getNitzTimeZoneDetectionSuccessful() {
        return mNitzTimeZoneDetectionSuccessful;
    }
@@ -510,5 +540,4 @@ public final class NitzStateMachineImpl implements NitzStateMachine {
    public String getSavedTimeZoneId() {
        return mSavedTimeZoneId;
    }

}
+96 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import org.junit.Before;
import org.junit.Test;

import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

public class NitzStateMachineImplTest extends TelephonyTest {

@@ -631,6 +632,101 @@ public class NitzStateMachineImplTest extends TelephonyTest {
        assertEquals(expectedZoneId, mNitzStateMachine.getSavedTimeZoneId());
    }

    @Test
    public void test_airplaneModeClearsState() throws Exception {
        Scenario scenario = UNITED_KINGDOM_SCENARIO.mutableCopy();
        long timeStepMillis = TimeUnit.HOURS.toMillis(3);

        Script script = new Script()
                .setRealtimeClockFromScenario(scenario)
                .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                .initializeTimeZoneDetectionEnabled(true)
                .initializeTimeZoneSetting(null);

        // Pre-flight: Simulate a device receiving signals that allow it to detect time and time
        // zone.
        TimestampedValue<NitzData> preflightNitzSignal = scenario.createNitzSignal();
        script.nitzReceived(preflightNitzSignal)
                .countryReceived(scenario.getNetworkCountryIsoCode())
                .verifyTimeSuggestedAndZoneSetAndReset(
                        scenario.createTimeSignal(), scenario.getTimeZoneId());

        // Demonstrate the NitzStateMachineImpl is "opinionated" about time zone: toggling auto-time
        // zone on should cause it to set the last known time zone again.
        // Note: Historically Android telephony time detection hasn't retained an opinion about time
        // so only the time zone is set. Also, NitzStateMachine doesn't pay attention to whether
        // auto-time is enabled; it is left to the system server service to decide whether to act on
        // the time suggestion if the settings allow.
        script.toggleTimeZoneDetectionEnabled(false)
                .verifyNothingWasSetAndReset()
                .toggleTimeZoneDetectionEnabled(true)
                .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());

        // Check state that NitzStateMachine must expose.
        assertEquals(preflightNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());

        // Boarded flight: Airplane mode turned on / time zone detection still enabled.
        // The NitzStateMachineImpl must lose all state and stop having an opinion about time zone.

        // Simulate the passage of time and update the device realtime clock.
        scenario.incrementTime(timeStepMillis);
        script.setRealtimeClockFromScenario(scenario);

        script.toggleAirplaneMode(true);

        // Check state that NitzStateMachine must expose.
        assertNull(mNitzStateMachine.getCachedNitzData());
        assertNull(mNitzStateMachine.getSavedTimeZoneId());

        // Verify there's no time zone opinion by toggling auto time zone off and on.
        script.toggleTimeZoneDetectionEnabled(false)
                .verifyNothingWasSetAndReset()
                .toggleTimeZoneDetectionEnabled(true)
                .verifyNothingWasSetAndReset();

        // During flight: Airplane mode turned off / time zone detection still enabled.
        // The NitzStateMachineImpl still must not have an opinion about time zone / hold any state.

        // Simulate the passage of time and update the device realtime clock.
        scenario.incrementTime(timeStepMillis);
        script.setRealtimeClockFromScenario(scenario);

        script.toggleAirplaneMode(false);

        // Verify there's still no opinion by toggling auto time zone off and on.
        script.toggleTimeZoneDetectionEnabled(false)
                .verifyNothingWasSetAndReset()
                .toggleTimeZoneDetectionEnabled(true)
                .verifyNothingWasSetAndReset();

        // Check the state that NitzStateMachine must expose.
        assertNull(mNitzStateMachine.getCachedNitzData());
        assertNull(mNitzStateMachine.getSavedTimeZoneId());

        // Post flight: Device has moved and receives new signals.

        // Simulate the passage of time and update the device realtime clock.
        scenario.incrementTime(timeStepMillis);
        script.setRealtimeClockFromScenario(scenario);

        // Simulate the movement to the destination.
        scenario.changeCountry(UNIQUE_US_ZONE_SCENARIO.getTimeZoneId(),
                UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode());

        // Simulate the device receiving NITZ signals again after the flight. Now the
        // NitzStateMachineImpl is opinionated again.
        TimestampedValue<NitzData> postFlightNitzSignal = scenario.createNitzSignal();
        script.countryReceived(scenario.getNetworkCountryIsoCode())
                .nitzReceived(postFlightNitzSignal)
                .verifyTimeSuggestedAndZoneSetAndReset(
                        scenario.createTimeSignal(), scenario.getTimeZoneId());

        // Check state that NitzStateMachine must expose.
        assertEquals(postFlightNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
        assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
    }

    /**
     * Asserts a test scenario has the properties we expect for NITZ-only lookup. There are
     * usually multiple zones that will share the same UTC offset so we get a low quality / low