Loading src/java/com/android/internal/telephony/NitzStateMachine.java +46 −14 Original line number Diff line number Diff line Loading @@ -89,17 +89,28 @@ public interface NitzStateMachine { interface DeviceState { /** * If elapsed time between two NITZ signals is less than this value then the second signal * can be ignored. * If the elapsed realtime between two NITZ signals is greater than this value then the * second signal cannot be ignored. */ int getNitzUpdateSpacingMillis(); /** * If UTC time between two NITZ signals is less than this value then the second signal can * be ignored. * If UTC time between two NITZ signals is greater than this value then the second signal * cannot be ignored. */ int getNitzUpdateDiffMillis(); /** * If the device connects to a telephony network and was disconnected from a telephony * network for less than this time, a previously received NITZ signal can be restored. * * <p>The restored NITZ may not be from the same network as the current network. It is * intended to be a relatively small value to allow for brief disconnections. Larger values * increase the likelihood that the device has moved to a different network and/or time * zone. */ int getNitzNetworkDisconnectRetentionMillis(); /** * Returns true if the {@code gsm.ignore-nitz} system property is set to "yes". */ Loading @@ -122,32 +133,53 @@ public interface NitzStateMachine { * {@hide} */ class DeviceStateImpl implements DeviceState { private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10; private final int mNitzUpdateSpacing; private static final int NITZ_UPDATE_DIFF_DEFAULT = 2000; private final int mNitzUpdateDiff; /** The default value to use for {@link #getNitzUpdateSpacingMillis()}. 10 minutes. */ private static final int NITZ_UPDATE_SPACING_MILLIS_DEFAULT = 1000 * 60 * 10; private final int mNitzUpdateSpacingMillis; /** The default value to use for {@link #getNitzUpdateDiffMillis()}. 2 seconds. */ private static final int NITZ_UPDATE_DIFF_MILLIS_DEFAULT = 2000; private final int mNitzUpdateDiffMillis; /** * The default value to use for {@link #getNitzNetworkDisconnectRetentionMillis()}. * 5 minutes. */ private static final int NITZ_NETWORK_DISCONNECT_RETENTION_MILLIS_DEFAULT = 1000 * 60 * 5; private final int mNitzNetworkDisconnectRetentionMillis; private final ContentResolver mCr; public DeviceStateImpl(Phone phone) { Context context = phone.getContext(); mCr = context.getContentResolver(); mNitzUpdateSpacing = SystemProperties.getInt("ro.nitz_update_spacing", NITZ_UPDATE_SPACING_DEFAULT); mNitzUpdateDiff = SystemProperties.getInt("ro.nitz_update_diff", NITZ_UPDATE_DIFF_DEFAULT); mNitzUpdateSpacingMillis = SystemProperties.getInt("ro.nitz_update_spacing", NITZ_UPDATE_SPACING_MILLIS_DEFAULT); mNitzUpdateDiffMillis = SystemProperties.getInt("ro.nitz_update_diff", NITZ_UPDATE_DIFF_MILLIS_DEFAULT); mNitzNetworkDisconnectRetentionMillis = SystemProperties.getInt("ro.nitz_network_disconnect_retention", NITZ_NETWORK_DISCONNECT_RETENTION_MILLIS_DEFAULT); } @Override public int getNitzUpdateSpacingMillis() { return Settings.Global.getInt(mCr, Settings.Global.NITZ_UPDATE_SPACING, mNitzUpdateSpacing); mNitzUpdateSpacingMillis); } @Override public int getNitzUpdateDiffMillis() { return Settings.Global.getInt(mCr, Settings.Global.NITZ_UPDATE_DIFF, mNitzUpdateDiff); return Settings.Global.getInt(mCr, Settings.Global.NITZ_UPDATE_DIFF, mNitzUpdateDiffMillis); } @Override public int getNitzNetworkDisconnectRetentionMillis() { return Settings.Global.getInt(mCr, Settings.Global.NITZ_NETWORK_DISCONNECT_RETENTION, mNitzNetworkDisconnectRetentionMillis); } @Override Loading src/java/com/android/internal/telephony/ServiceStateTracker.java +2 −0 Original line number Diff line number Diff line Loading @@ -3590,10 +3590,12 @@ public class ServiceStateTracker extends Handler { if (hasRegistered) { mNetworkAttachedRegistrants.notifyRegistrants(); mNitzState.handleNetworkAvailable(); } if (hasDeregistered) { mNetworkDetachedRegistrants.notifyRegistrants(); mNitzState.handleNetworkUnavailable(); } if (hasCssIndicatorChanged) { Loading src/java/com/android/internal/telephony/nitz/NitzStateMachineImpl.java +99 −52 Original line number Diff line number Diff line Loading @@ -98,14 +98,15 @@ public final class NitzStateMachineImpl implements NitzStateMachine { // Miscellaneous dependencies and helpers not related to detection state. private final int mSlotIndex; @NonNull private final DeviceState mDeviceState; /** Applied to NITZ signals during input filtering. */ private final NitzSignalInputFilterPredicate mNitzSignalInputFilter; @NonNull private final NitzSignalInputFilterPredicate mNitzSignalInputFilter; /** * Creates a {@link TelephonyTimeZoneSuggestion} for passing to the time zone detection service. */ private final TimeZoneSuggester mTimeZoneSuggester; @NonNull private final TimeZoneSuggester mTimeZoneSuggester; /** A facade to the time / time zone detection services. */ private final TimeServiceHelper mTimeServiceHelper; @NonNull private final TimeServiceHelper mTimeServiceHelper; // Shared detection state. Loading @@ -114,8 +115,14 @@ public final class NitzStateMachineImpl implements NitzStateMachine { * input filtering (e.g. rate limiting) and provides the NITZ information when time / time zone * needs to be recalculated when something else has changed. */ @Nullable private NitzSignal mLatestNitzSignal; @Nullable private NitzSignal mLatestNitzSignal; /** * The last NITZ received, and the time when it was cleared. Used to restore the NITZ for * transient network disconnections. This can be null, but the value held when it isn't will not * be. */ @Nullable private TimestampedValue<NitzSignal> mLastNitzSignalCleared; // Time Zone detection state. Loading @@ -124,7 +131,7 @@ public final class NitzStateMachineImpl implements NitzStateMachine { * (lower case), empty (test network) or null (no country detected). A country code is required * to determine time zone except when on a test network. */ private String mCountryIsoCode; @Nullable private String mCountryIsoCode; /** * Creates an instance for the supplied {@link Phone}. Loading @@ -141,7 +148,7 @@ public final class NitzStateMachineImpl implements NitzStateMachine { NitzSignalInputFilterPredicate nitzSignalFilter = NitzSignalInputFilterPredicateFactory.create(phone.getContext(), deviceState); return new NitzStateMachineImpl( slotIndex, nitzSignalFilter, timeZoneSuggester, newTimeServiceHelper); slotIndex, deviceState, nitzSignalFilter, timeZoneSuggester, newTimeServiceHelper); } /** Loading @@ -150,10 +157,12 @@ public final class NitzStateMachineImpl implements NitzStateMachine { */ @VisibleForTesting public NitzStateMachineImpl(int slotIndex, @NonNull DeviceState deviceState, @NonNull NitzSignalInputFilterPredicate nitzSignalInputFilter, @NonNull TimeZoneSuggester timeZoneSuggester, @NonNull TimeServiceHelper newTimeServiceHelper) { mSlotIndex = slotIndex; mDeviceState = Objects.requireNonNull(deviceState); mTimeZoneSuggester = Objects.requireNonNull(timeZoneSuggester); mTimeServiceHelper = Objects.requireNonNull(newTimeServiceHelper); mNitzSignalInputFilter = Objects.requireNonNull(nitzSignalInputFilter); Loading @@ -161,41 +170,14 @@ public final class NitzStateMachineImpl implements NitzStateMachine { @Override public void handleNetworkAvailable() { // We no longer do any useful work here: we assume handleNetworkUnavailable() is reliable. // TODO: Remove this method when all implementations do nothing. String reason = "handleNetworkAvailable"; restoreNetworkStateAndRerunDetection(reason); } @Override public void handleNetworkUnavailable() { String reason = "handleNetworkUnavailable()"; clearNetworkStateAndRerunDetection(reason); } private void clearNetworkStateAndRerunDetection(String reason) { if (mLatestNitzSignal == null) { // The network state is already empty so there's no need to do anything. if (DBG) { Rlog.d(LOG_TAG, reason + ": mLatestNitzSignal was already null. Nothing to do."); } return; } // The previous NITZ signal received is now invalid so clear it. mLatestNitzSignal = null; // countryIsoCode can be assigned null here, in which case the doTimeZoneDetection() call // below will do nothing, which is ok as nothing will have changed. String countryIsoCode = mCountryIsoCode; if (DBG) { Rlog.d(LOG_TAG, reason + ": countryIsoCode=" + countryIsoCode); } // Generate a new time zone suggestion (which could be an empty suggestion) and update the // service as needed. doTimeZoneDetection(countryIsoCode, null /* nitzSignal */, reason); // Generate a new time suggestion and update the service as needed. doTimeDetection(null /* nitzSignal */, reason); String reason = "handleNetworkUnavailable"; clearNetworkStateAndRerunDetection(reason, false /* fullyClearNitz */); } @Override Loading Loading @@ -224,19 +206,20 @@ public final class NitzStateMachineImpl implements NitzStateMachine { // Generate a new time zone suggestion and update the service as needed. doTimeZoneDetection(null /* countryIsoCode */, mLatestNitzSignal, "handleCountryUnavailable()"); "handleCountryUnavailable"); } @Override public void handleNitzReceived(@NonNull NitzSignal nitzSignal) { if (DBG) { Rlog.d(LOG_TAG, "handleNitzReceived: nitzSignal=" + nitzSignal); } Objects.requireNonNull(nitzSignal); // Perform input filtering to filter bad data and avoid processing signals too often. NitzSignal previousNitzSignal = mLatestNitzSignal; if (!mNitzSignalInputFilter.mustProcessNitzSignal(previousNitzSignal, nitzSignal)) { if (DBG) { Rlog.d(LOG_TAG, "handleNitzReceived: previousNitzSignal=" + previousNitzSignal + ", nitzSignal=" + nitzSignal + ": NITZ filtered"); } return; } Loading @@ -244,13 +227,7 @@ public final class NitzStateMachineImpl implements NitzStateMachine { mLatestNitzSignal = nitzSignal; String reason = "handleNitzReceived(" + nitzSignal + ")"; // Generate a new time zone suggestion and update the service as needed. String countryIsoCode = mCountryIsoCode; doTimeZoneDetection(countryIsoCode, nitzSignal, reason); // Generate a new time suggestion and update the service as needed. doTimeDetection(nitzSignal, reason); runDetection(reason); } @Override Loading @@ -272,15 +249,84 @@ public final class NitzStateMachineImpl implements NitzStateMachine { mCountryIsoCode = null; String reason = "handleAirplaneModeChanged(" + on + ")"; clearNetworkStateAndRerunDetection(reason); clearNetworkStateAndRerunDetection(reason, true /* fullyClearNitz */); } private void restoreNetworkStateAndRerunDetection(String reason) { // Restore the last NITZ signal if the network has been unavailable for only a short period. if (mLastNitzSignalCleared == null) { if (DBG) { Rlog.d(LOG_TAG, reason + ": mLastNitzSignalCleared is null."); } // Nothing has changed. No work to do. return; } long timeSinceNitzClearedMillis = mDeviceState.elapsedRealtimeMillis() - mLastNitzSignalCleared.getReferenceTimeMillis(); boolean canRestoreNitz = timeSinceNitzClearedMillis < mDeviceState.getNitzNetworkDisconnectRetentionMillis(); if (canRestoreNitz) { reason = reason + ", mLatestNitzSignal restored from mLastNitzSignalCleared=" + mLastNitzSignalCleared.getValue(); mLatestNitzSignal = mLastNitzSignalCleared.getValue(); runDetection(reason); } else { if (DBG) { Rlog.d(LOG_TAG, reason + ": mLastNitzSignalCleared is too old."); } } } private void clearNetworkStateAndRerunDetection(String reason, boolean fullyClearNitz) { if (mLatestNitzSignal == null) { if (fullyClearNitz) { mLastNitzSignalCleared = null; } // The network state is already empty so there's no need to do anything. if (DBG) { Rlog.d(LOG_TAG, reason + ": mLatestNitzSignal was already null. Nothing to do."); } return; } if (fullyClearNitz) { mLastNitzSignalCleared = null; } else { mLastNitzSignalCleared = new TimestampedValue<>( mDeviceState.elapsedRealtimeMillis(), mLatestNitzSignal); } mLatestNitzSignal = null; runDetection(reason); } private void runDetection(String reason) { // countryIsoCode can be assigned null here, in which case the doTimeZoneDetection() call // below will do nothing. String countryIsoCode = mCountryIsoCode; NitzSignal nitzSignal = mLatestNitzSignal; if (DBG) { Rlog.d(LOG_TAG, "runDetection: reason=" + reason + ", countryIsoCode=" + countryIsoCode + ", nitzSignal=" + nitzSignal); } // Generate a new time zone suggestion (which could be an empty suggestion) and update the // service as needed. doTimeZoneDetection(countryIsoCode, nitzSignal, reason); // Generate a new time suggestion and update the service as needed. doTimeDetection(nitzSignal, reason); } /** * Perform a round of time zone detection and notify the time zone detection service as needed. */ private void doTimeZoneDetection( @Nullable String countryIsoCode, @Nullable NitzSignal nitzSignal, @Nullable String countryIsoCode, @Nullable NitzSignal nitzSignal, @NonNull String reason) { try { Objects.requireNonNull(reason); Loading Loading @@ -348,6 +394,7 @@ public final class NitzStateMachineImpl implements NitzStateMachine { mTimeServiceHelper.dumpLogs(ipw); } @VisibleForTesting @Nullable public NitzData getCachedNitzData() { return mLatestNitzSignal != null ? mLatestNitzSignal.getNitzData() : null; Loading tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java +106 −12 Original line number Diff line number Diff line Loading @@ -90,7 +90,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { mRealTimeZoneSuggester = new TimeZoneSuggesterImpl(mFakeDeviceState, timeZoneLookupHelper); mNitzStateMachineImpl = new NitzStateMachineImpl( SLOT_INDEX, mFakeNitzSignalInputFilter, mRealTimeZoneSuggester, SLOT_INDEX, mFakeDeviceState, mFakeNitzSignalInputFilter, mRealTimeZoneSuggester, mFakeTimeServiceHelper); TelephonyTest.logd("NewNitzStateMachineImplTest -Setup!"); Loading Loading @@ -298,7 +298,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( expectedPreFlightTimeSuggestion, expectedPreFlightTimeZoneSuggestion); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertEquals(preFlightNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); // Boarded flight: Airplane mode turned on / time zone detection still enabled. Loading @@ -316,7 +316,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( EMPTY_TIME_SUGGESTION, EMPTY_TIME_ZONE_SUGGESTION); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertNull(mNitzStateMachineImpl.getCachedNitzData()); // During flight: Airplane mode turned off / time zone detection still enabled. Loading @@ -332,7 +332,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { // Verify nothing was suggested: The last suggestion was empty so nothing has changed. script.verifyNothingWasSuggested(); // Check the state that NitzStateMachine must expose. // Check the state that NitzStateMachineImpl exposes for tests. assertNull(mNitzStateMachineImpl.getCachedNitzData()); // Post flight: Device has moved and receives new signals. Loading Loading @@ -362,7 +362,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( expectedPostFlightTimeSuggestion, expectedPostFlightTimeZoneSuggestion); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertEquals(postFlightNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); } Loading @@ -370,11 +370,15 @@ public class NitzStateMachineImplTest extends TelephonyTest { * Confirm losing the network / NITZ doesn't clear country state. */ @Test public void test_handleNetworkUnavailableClearsNetworkState() throws Exception { public void test_handleNetworkUnavailableClearsNetworkState_noRetention() throws Exception { Scenario scenario = UNIQUE_US_ZONE_SCENARIO1.mutableCopy(); int timeStepMillis = (int) TimeUnit.HOURS.toMillis(3); String countryIsoCode = scenario.getNetworkCountryIsoCode(); // Set retention threshold to zero to prevent NITZ being saved / restored when the network // becomes unavailable / available again. mFakeDeviceState.setNitzNetworkDisconnectRetentionMillis(0); Script script = new Script() .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME) .networkAvailable(); Loading @@ -396,7 +400,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( expectedInitialTimeSuggestion, expectedInitialTimeZoneSuggestion); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertEquals(initialNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); // Simulate the passage of time and update the device realtime clock. Loading @@ -413,7 +417,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( EMPTY_TIME_SUGGESTION, expectedMiddleTimeZoneSuggestion); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertNull(mNitzStateMachineImpl.getCachedNitzData()); // Simulate the passage of time and update the device realtime clock. Loading @@ -424,7 +428,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.networkAvailable() .verifyNothingWasSuggested(); // Check the state that NitzStateMachine must expose. // Check the state that NitzStateMachineImpl exposes for tests. assertNull(mNitzStateMachineImpl.getCachedNitzData()); // Simulate the passage of time and update the device realtime clock. Loading @@ -446,7 +450,97 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( expectedFinalTimeSuggestion, expectedFinalTimeZoneSuggestion); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertEquals(finalNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); } /** * Tests the NITZ retention / restore behavior when networkAvailable() is called after * networkUnavailable() inside the retention threshold. */ @Test public void test_handleNetworkUnavailableClearsNetworkState_withinRetentionThreshold() throws Exception { Scenario scenario = UNIQUE_US_ZONE_SCENARIO1.mutableCopy(); int timeStepMillis = (int) TimeUnit.HOURS.toMillis(3); String countryIsoCode = scenario.getNetworkCountryIsoCode(); // Set the retention threshold to effectively infinite. mFakeDeviceState.setNitzNetworkDisconnectRetentionMillis(Integer.MAX_VALUE); Script script = new Script() .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME) .networkAvailable(); // Simulate a device receiving signals that allow it to detect time and time zone. NitzSignal initialNitzSignal = scenario.createNitzSignal(mFakeDeviceState.elapsedRealtimeMillis(), ARBITRARY_AGE); TelephonyTimeSuggestion expectedInitialTimeSuggestion = createTimeSuggestionFromNitzSignal(SLOT_INDEX, initialNitzSignal); // Simulate receiving the NITZ signal and country. script.nitzReceived(initialNitzSignal) .countryReceived(countryIsoCode); // Verify the state machine did the right thing. TelephonyTimeZoneSuggestion expectedInitialTimeZoneSuggestion = mRealTimeZoneSuggester.getTimeZoneSuggestion( SLOT_INDEX, countryIsoCode, initialNitzSignal); script.verifyTimeAndTimeZoneSuggestedAndReset( expectedInitialTimeSuggestion, expectedInitialTimeZoneSuggestion); // Check state that NitzStateMachineImpl exposes for tests. assertEquals(initialNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); // Simulate the passage of time and update the device realtime clock. scenario.incrementTime(timeStepMillis); script.incrementTime(timeStepMillis); // Simulate network being lost. script.networkUnavailable(); // Check the "no NITZ" time and time zone suggestions are made. TelephonyTimeZoneSuggestion expectedMiddleTimeZoneSuggestion = mRealTimeZoneSuggester.getTimeZoneSuggestion( SLOT_INDEX, countryIsoCode, null /* nitzSignal */); script.verifyTimeAndTimeZoneSuggestedAndReset( EMPTY_TIME_SUGGESTION, expectedMiddleTimeZoneSuggestion); // Check state that NitzStateMachineImpl exposes for tests. assertNull(mNitzStateMachineImpl.getCachedNitzData()); // Simulate the passage of time and update the device realtime clock. scenario.incrementTime(timeStepMillis); script.incrementTime(timeStepMillis); // Simulate the network being found. As we are inside the NITZ retention threshold, the // initial NITZ signal should be restored, and the same suggestion made. script.networkAvailable() .verifyTimeAndTimeZoneSuggestedAndReset( expectedInitialTimeSuggestion, expectedInitialTimeZoneSuggestion); // Check the state that NitzStateMachineImpl exposes for tests. assertEquals(initialNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); // Simulate the passage of time and update the device realtime clock. scenario.incrementTime(timeStepMillis); script.incrementTime(timeStepMillis); // Simulate the device receiving another NITZ signal. NitzSignal finalNitzSignal = scenario.createNitzSignal(mFakeDeviceState.elapsedRealtimeMillis(), ARBITRARY_AGE); script.nitzReceived(finalNitzSignal); // Verify the state machine did the right thing. TelephonyTimeSuggestion expectedFinalTimeSuggestion = createTimeSuggestionFromNitzSignal(SLOT_INDEX, finalNitzSignal); TelephonyTimeZoneSuggestion expectedFinalTimeZoneSuggestion = mRealTimeZoneSuggester.getTimeZoneSuggestion( SLOT_INDEX, countryIsoCode, finalNitzSignal); script.verifyTimeAndTimeZoneSuggestedAndReset( expectedFinalTimeSuggestion, expectedFinalTimeZoneSuggestion); // Check state that NitzStateMachineImpl exposes for tests. assertEquals(finalNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); } Loading Loading @@ -477,7 +571,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( expectedTimeSuggestion, expectedTimeZoneSuggestion2); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertEquals(nitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); // Simulate the country becoming unavailable and verify the state machine does the right Loading @@ -488,7 +582,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { SLOT_INDEX, null /* countryIsoCode */, nitzSignal); script.verifyOnlyTimeZoneWasSuggestedAndReset(expectedTimeZoneSuggestion3); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertEquals(nitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); } Loading tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineTestSupport.java +12 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
src/java/com/android/internal/telephony/NitzStateMachine.java +46 −14 Original line number Diff line number Diff line Loading @@ -89,17 +89,28 @@ public interface NitzStateMachine { interface DeviceState { /** * If elapsed time between two NITZ signals is less than this value then the second signal * can be ignored. * If the elapsed realtime between two NITZ signals is greater than this value then the * second signal cannot be ignored. */ int getNitzUpdateSpacingMillis(); /** * If UTC time between two NITZ signals is less than this value then the second signal can * be ignored. * If UTC time between two NITZ signals is greater than this value then the second signal * cannot be ignored. */ int getNitzUpdateDiffMillis(); /** * If the device connects to a telephony network and was disconnected from a telephony * network for less than this time, a previously received NITZ signal can be restored. * * <p>The restored NITZ may not be from the same network as the current network. It is * intended to be a relatively small value to allow for brief disconnections. Larger values * increase the likelihood that the device has moved to a different network and/or time * zone. */ int getNitzNetworkDisconnectRetentionMillis(); /** * Returns true if the {@code gsm.ignore-nitz} system property is set to "yes". */ Loading @@ -122,32 +133,53 @@ public interface NitzStateMachine { * {@hide} */ class DeviceStateImpl implements DeviceState { private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10; private final int mNitzUpdateSpacing; private static final int NITZ_UPDATE_DIFF_DEFAULT = 2000; private final int mNitzUpdateDiff; /** The default value to use for {@link #getNitzUpdateSpacingMillis()}. 10 minutes. */ private static final int NITZ_UPDATE_SPACING_MILLIS_DEFAULT = 1000 * 60 * 10; private final int mNitzUpdateSpacingMillis; /** The default value to use for {@link #getNitzUpdateDiffMillis()}. 2 seconds. */ private static final int NITZ_UPDATE_DIFF_MILLIS_DEFAULT = 2000; private final int mNitzUpdateDiffMillis; /** * The default value to use for {@link #getNitzNetworkDisconnectRetentionMillis()}. * 5 minutes. */ private static final int NITZ_NETWORK_DISCONNECT_RETENTION_MILLIS_DEFAULT = 1000 * 60 * 5; private final int mNitzNetworkDisconnectRetentionMillis; private final ContentResolver mCr; public DeviceStateImpl(Phone phone) { Context context = phone.getContext(); mCr = context.getContentResolver(); mNitzUpdateSpacing = SystemProperties.getInt("ro.nitz_update_spacing", NITZ_UPDATE_SPACING_DEFAULT); mNitzUpdateDiff = SystemProperties.getInt("ro.nitz_update_diff", NITZ_UPDATE_DIFF_DEFAULT); mNitzUpdateSpacingMillis = SystemProperties.getInt("ro.nitz_update_spacing", NITZ_UPDATE_SPACING_MILLIS_DEFAULT); mNitzUpdateDiffMillis = SystemProperties.getInt("ro.nitz_update_diff", NITZ_UPDATE_DIFF_MILLIS_DEFAULT); mNitzNetworkDisconnectRetentionMillis = SystemProperties.getInt("ro.nitz_network_disconnect_retention", NITZ_NETWORK_DISCONNECT_RETENTION_MILLIS_DEFAULT); } @Override public int getNitzUpdateSpacingMillis() { return Settings.Global.getInt(mCr, Settings.Global.NITZ_UPDATE_SPACING, mNitzUpdateSpacing); mNitzUpdateSpacingMillis); } @Override public int getNitzUpdateDiffMillis() { return Settings.Global.getInt(mCr, Settings.Global.NITZ_UPDATE_DIFF, mNitzUpdateDiff); return Settings.Global.getInt(mCr, Settings.Global.NITZ_UPDATE_DIFF, mNitzUpdateDiffMillis); } @Override public int getNitzNetworkDisconnectRetentionMillis() { return Settings.Global.getInt(mCr, Settings.Global.NITZ_NETWORK_DISCONNECT_RETENTION, mNitzNetworkDisconnectRetentionMillis); } @Override Loading
src/java/com/android/internal/telephony/ServiceStateTracker.java +2 −0 Original line number Diff line number Diff line Loading @@ -3590,10 +3590,12 @@ public class ServiceStateTracker extends Handler { if (hasRegistered) { mNetworkAttachedRegistrants.notifyRegistrants(); mNitzState.handleNetworkAvailable(); } if (hasDeregistered) { mNetworkDetachedRegistrants.notifyRegistrants(); mNitzState.handleNetworkUnavailable(); } if (hasCssIndicatorChanged) { Loading
src/java/com/android/internal/telephony/nitz/NitzStateMachineImpl.java +99 −52 Original line number Diff line number Diff line Loading @@ -98,14 +98,15 @@ public final class NitzStateMachineImpl implements NitzStateMachine { // Miscellaneous dependencies and helpers not related to detection state. private final int mSlotIndex; @NonNull private final DeviceState mDeviceState; /** Applied to NITZ signals during input filtering. */ private final NitzSignalInputFilterPredicate mNitzSignalInputFilter; @NonNull private final NitzSignalInputFilterPredicate mNitzSignalInputFilter; /** * Creates a {@link TelephonyTimeZoneSuggestion} for passing to the time zone detection service. */ private final TimeZoneSuggester mTimeZoneSuggester; @NonNull private final TimeZoneSuggester mTimeZoneSuggester; /** A facade to the time / time zone detection services. */ private final TimeServiceHelper mTimeServiceHelper; @NonNull private final TimeServiceHelper mTimeServiceHelper; // Shared detection state. Loading @@ -114,8 +115,14 @@ public final class NitzStateMachineImpl implements NitzStateMachine { * input filtering (e.g. rate limiting) and provides the NITZ information when time / time zone * needs to be recalculated when something else has changed. */ @Nullable private NitzSignal mLatestNitzSignal; @Nullable private NitzSignal mLatestNitzSignal; /** * The last NITZ received, and the time when it was cleared. Used to restore the NITZ for * transient network disconnections. This can be null, but the value held when it isn't will not * be. */ @Nullable private TimestampedValue<NitzSignal> mLastNitzSignalCleared; // Time Zone detection state. Loading @@ -124,7 +131,7 @@ public final class NitzStateMachineImpl implements NitzStateMachine { * (lower case), empty (test network) or null (no country detected). A country code is required * to determine time zone except when on a test network. */ private String mCountryIsoCode; @Nullable private String mCountryIsoCode; /** * Creates an instance for the supplied {@link Phone}. Loading @@ -141,7 +148,7 @@ public final class NitzStateMachineImpl implements NitzStateMachine { NitzSignalInputFilterPredicate nitzSignalFilter = NitzSignalInputFilterPredicateFactory.create(phone.getContext(), deviceState); return new NitzStateMachineImpl( slotIndex, nitzSignalFilter, timeZoneSuggester, newTimeServiceHelper); slotIndex, deviceState, nitzSignalFilter, timeZoneSuggester, newTimeServiceHelper); } /** Loading @@ -150,10 +157,12 @@ public final class NitzStateMachineImpl implements NitzStateMachine { */ @VisibleForTesting public NitzStateMachineImpl(int slotIndex, @NonNull DeviceState deviceState, @NonNull NitzSignalInputFilterPredicate nitzSignalInputFilter, @NonNull TimeZoneSuggester timeZoneSuggester, @NonNull TimeServiceHelper newTimeServiceHelper) { mSlotIndex = slotIndex; mDeviceState = Objects.requireNonNull(deviceState); mTimeZoneSuggester = Objects.requireNonNull(timeZoneSuggester); mTimeServiceHelper = Objects.requireNonNull(newTimeServiceHelper); mNitzSignalInputFilter = Objects.requireNonNull(nitzSignalInputFilter); Loading @@ -161,41 +170,14 @@ public final class NitzStateMachineImpl implements NitzStateMachine { @Override public void handleNetworkAvailable() { // We no longer do any useful work here: we assume handleNetworkUnavailable() is reliable. // TODO: Remove this method when all implementations do nothing. String reason = "handleNetworkAvailable"; restoreNetworkStateAndRerunDetection(reason); } @Override public void handleNetworkUnavailable() { String reason = "handleNetworkUnavailable()"; clearNetworkStateAndRerunDetection(reason); } private void clearNetworkStateAndRerunDetection(String reason) { if (mLatestNitzSignal == null) { // The network state is already empty so there's no need to do anything. if (DBG) { Rlog.d(LOG_TAG, reason + ": mLatestNitzSignal was already null. Nothing to do."); } return; } // The previous NITZ signal received is now invalid so clear it. mLatestNitzSignal = null; // countryIsoCode can be assigned null here, in which case the doTimeZoneDetection() call // below will do nothing, which is ok as nothing will have changed. String countryIsoCode = mCountryIsoCode; if (DBG) { Rlog.d(LOG_TAG, reason + ": countryIsoCode=" + countryIsoCode); } // Generate a new time zone suggestion (which could be an empty suggestion) and update the // service as needed. doTimeZoneDetection(countryIsoCode, null /* nitzSignal */, reason); // Generate a new time suggestion and update the service as needed. doTimeDetection(null /* nitzSignal */, reason); String reason = "handleNetworkUnavailable"; clearNetworkStateAndRerunDetection(reason, false /* fullyClearNitz */); } @Override Loading Loading @@ -224,19 +206,20 @@ public final class NitzStateMachineImpl implements NitzStateMachine { // Generate a new time zone suggestion and update the service as needed. doTimeZoneDetection(null /* countryIsoCode */, mLatestNitzSignal, "handleCountryUnavailable()"); "handleCountryUnavailable"); } @Override public void handleNitzReceived(@NonNull NitzSignal nitzSignal) { if (DBG) { Rlog.d(LOG_TAG, "handleNitzReceived: nitzSignal=" + nitzSignal); } Objects.requireNonNull(nitzSignal); // Perform input filtering to filter bad data and avoid processing signals too often. NitzSignal previousNitzSignal = mLatestNitzSignal; if (!mNitzSignalInputFilter.mustProcessNitzSignal(previousNitzSignal, nitzSignal)) { if (DBG) { Rlog.d(LOG_TAG, "handleNitzReceived: previousNitzSignal=" + previousNitzSignal + ", nitzSignal=" + nitzSignal + ": NITZ filtered"); } return; } Loading @@ -244,13 +227,7 @@ public final class NitzStateMachineImpl implements NitzStateMachine { mLatestNitzSignal = nitzSignal; String reason = "handleNitzReceived(" + nitzSignal + ")"; // Generate a new time zone suggestion and update the service as needed. String countryIsoCode = mCountryIsoCode; doTimeZoneDetection(countryIsoCode, nitzSignal, reason); // Generate a new time suggestion and update the service as needed. doTimeDetection(nitzSignal, reason); runDetection(reason); } @Override Loading @@ -272,15 +249,84 @@ public final class NitzStateMachineImpl implements NitzStateMachine { mCountryIsoCode = null; String reason = "handleAirplaneModeChanged(" + on + ")"; clearNetworkStateAndRerunDetection(reason); clearNetworkStateAndRerunDetection(reason, true /* fullyClearNitz */); } private void restoreNetworkStateAndRerunDetection(String reason) { // Restore the last NITZ signal if the network has been unavailable for only a short period. if (mLastNitzSignalCleared == null) { if (DBG) { Rlog.d(LOG_TAG, reason + ": mLastNitzSignalCleared is null."); } // Nothing has changed. No work to do. return; } long timeSinceNitzClearedMillis = mDeviceState.elapsedRealtimeMillis() - mLastNitzSignalCleared.getReferenceTimeMillis(); boolean canRestoreNitz = timeSinceNitzClearedMillis < mDeviceState.getNitzNetworkDisconnectRetentionMillis(); if (canRestoreNitz) { reason = reason + ", mLatestNitzSignal restored from mLastNitzSignalCleared=" + mLastNitzSignalCleared.getValue(); mLatestNitzSignal = mLastNitzSignalCleared.getValue(); runDetection(reason); } else { if (DBG) { Rlog.d(LOG_TAG, reason + ": mLastNitzSignalCleared is too old."); } } } private void clearNetworkStateAndRerunDetection(String reason, boolean fullyClearNitz) { if (mLatestNitzSignal == null) { if (fullyClearNitz) { mLastNitzSignalCleared = null; } // The network state is already empty so there's no need to do anything. if (DBG) { Rlog.d(LOG_TAG, reason + ": mLatestNitzSignal was already null. Nothing to do."); } return; } if (fullyClearNitz) { mLastNitzSignalCleared = null; } else { mLastNitzSignalCleared = new TimestampedValue<>( mDeviceState.elapsedRealtimeMillis(), mLatestNitzSignal); } mLatestNitzSignal = null; runDetection(reason); } private void runDetection(String reason) { // countryIsoCode can be assigned null here, in which case the doTimeZoneDetection() call // below will do nothing. String countryIsoCode = mCountryIsoCode; NitzSignal nitzSignal = mLatestNitzSignal; if (DBG) { Rlog.d(LOG_TAG, "runDetection: reason=" + reason + ", countryIsoCode=" + countryIsoCode + ", nitzSignal=" + nitzSignal); } // Generate a new time zone suggestion (which could be an empty suggestion) and update the // service as needed. doTimeZoneDetection(countryIsoCode, nitzSignal, reason); // Generate a new time suggestion and update the service as needed. doTimeDetection(nitzSignal, reason); } /** * Perform a round of time zone detection and notify the time zone detection service as needed. */ private void doTimeZoneDetection( @Nullable String countryIsoCode, @Nullable NitzSignal nitzSignal, @Nullable String countryIsoCode, @Nullable NitzSignal nitzSignal, @NonNull String reason) { try { Objects.requireNonNull(reason); Loading Loading @@ -348,6 +394,7 @@ public final class NitzStateMachineImpl implements NitzStateMachine { mTimeServiceHelper.dumpLogs(ipw); } @VisibleForTesting @Nullable public NitzData getCachedNitzData() { return mLatestNitzSignal != null ? mLatestNitzSignal.getNitzData() : null; Loading
tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java +106 −12 Original line number Diff line number Diff line Loading @@ -90,7 +90,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { mRealTimeZoneSuggester = new TimeZoneSuggesterImpl(mFakeDeviceState, timeZoneLookupHelper); mNitzStateMachineImpl = new NitzStateMachineImpl( SLOT_INDEX, mFakeNitzSignalInputFilter, mRealTimeZoneSuggester, SLOT_INDEX, mFakeDeviceState, mFakeNitzSignalInputFilter, mRealTimeZoneSuggester, mFakeTimeServiceHelper); TelephonyTest.logd("NewNitzStateMachineImplTest -Setup!"); Loading Loading @@ -298,7 +298,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( expectedPreFlightTimeSuggestion, expectedPreFlightTimeZoneSuggestion); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertEquals(preFlightNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); // Boarded flight: Airplane mode turned on / time zone detection still enabled. Loading @@ -316,7 +316,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( EMPTY_TIME_SUGGESTION, EMPTY_TIME_ZONE_SUGGESTION); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertNull(mNitzStateMachineImpl.getCachedNitzData()); // During flight: Airplane mode turned off / time zone detection still enabled. Loading @@ -332,7 +332,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { // Verify nothing was suggested: The last suggestion was empty so nothing has changed. script.verifyNothingWasSuggested(); // Check the state that NitzStateMachine must expose. // Check the state that NitzStateMachineImpl exposes for tests. assertNull(mNitzStateMachineImpl.getCachedNitzData()); // Post flight: Device has moved and receives new signals. Loading Loading @@ -362,7 +362,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( expectedPostFlightTimeSuggestion, expectedPostFlightTimeZoneSuggestion); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertEquals(postFlightNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); } Loading @@ -370,11 +370,15 @@ public class NitzStateMachineImplTest extends TelephonyTest { * Confirm losing the network / NITZ doesn't clear country state. */ @Test public void test_handleNetworkUnavailableClearsNetworkState() throws Exception { public void test_handleNetworkUnavailableClearsNetworkState_noRetention() throws Exception { Scenario scenario = UNIQUE_US_ZONE_SCENARIO1.mutableCopy(); int timeStepMillis = (int) TimeUnit.HOURS.toMillis(3); String countryIsoCode = scenario.getNetworkCountryIsoCode(); // Set retention threshold to zero to prevent NITZ being saved / restored when the network // becomes unavailable / available again. mFakeDeviceState.setNitzNetworkDisconnectRetentionMillis(0); Script script = new Script() .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME) .networkAvailable(); Loading @@ -396,7 +400,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( expectedInitialTimeSuggestion, expectedInitialTimeZoneSuggestion); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertEquals(initialNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); // Simulate the passage of time and update the device realtime clock. Loading @@ -413,7 +417,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( EMPTY_TIME_SUGGESTION, expectedMiddleTimeZoneSuggestion); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertNull(mNitzStateMachineImpl.getCachedNitzData()); // Simulate the passage of time and update the device realtime clock. Loading @@ -424,7 +428,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.networkAvailable() .verifyNothingWasSuggested(); // Check the state that NitzStateMachine must expose. // Check the state that NitzStateMachineImpl exposes for tests. assertNull(mNitzStateMachineImpl.getCachedNitzData()); // Simulate the passage of time and update the device realtime clock. Loading @@ -446,7 +450,97 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( expectedFinalTimeSuggestion, expectedFinalTimeZoneSuggestion); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertEquals(finalNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); } /** * Tests the NITZ retention / restore behavior when networkAvailable() is called after * networkUnavailable() inside the retention threshold. */ @Test public void test_handleNetworkUnavailableClearsNetworkState_withinRetentionThreshold() throws Exception { Scenario scenario = UNIQUE_US_ZONE_SCENARIO1.mutableCopy(); int timeStepMillis = (int) TimeUnit.HOURS.toMillis(3); String countryIsoCode = scenario.getNetworkCountryIsoCode(); // Set the retention threshold to effectively infinite. mFakeDeviceState.setNitzNetworkDisconnectRetentionMillis(Integer.MAX_VALUE); Script script = new Script() .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME) .networkAvailable(); // Simulate a device receiving signals that allow it to detect time and time zone. NitzSignal initialNitzSignal = scenario.createNitzSignal(mFakeDeviceState.elapsedRealtimeMillis(), ARBITRARY_AGE); TelephonyTimeSuggestion expectedInitialTimeSuggestion = createTimeSuggestionFromNitzSignal(SLOT_INDEX, initialNitzSignal); // Simulate receiving the NITZ signal and country. script.nitzReceived(initialNitzSignal) .countryReceived(countryIsoCode); // Verify the state machine did the right thing. TelephonyTimeZoneSuggestion expectedInitialTimeZoneSuggestion = mRealTimeZoneSuggester.getTimeZoneSuggestion( SLOT_INDEX, countryIsoCode, initialNitzSignal); script.verifyTimeAndTimeZoneSuggestedAndReset( expectedInitialTimeSuggestion, expectedInitialTimeZoneSuggestion); // Check state that NitzStateMachineImpl exposes for tests. assertEquals(initialNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); // Simulate the passage of time and update the device realtime clock. scenario.incrementTime(timeStepMillis); script.incrementTime(timeStepMillis); // Simulate network being lost. script.networkUnavailable(); // Check the "no NITZ" time and time zone suggestions are made. TelephonyTimeZoneSuggestion expectedMiddleTimeZoneSuggestion = mRealTimeZoneSuggester.getTimeZoneSuggestion( SLOT_INDEX, countryIsoCode, null /* nitzSignal */); script.verifyTimeAndTimeZoneSuggestedAndReset( EMPTY_TIME_SUGGESTION, expectedMiddleTimeZoneSuggestion); // Check state that NitzStateMachineImpl exposes for tests. assertNull(mNitzStateMachineImpl.getCachedNitzData()); // Simulate the passage of time and update the device realtime clock. scenario.incrementTime(timeStepMillis); script.incrementTime(timeStepMillis); // Simulate the network being found. As we are inside the NITZ retention threshold, the // initial NITZ signal should be restored, and the same suggestion made. script.networkAvailable() .verifyTimeAndTimeZoneSuggestedAndReset( expectedInitialTimeSuggestion, expectedInitialTimeZoneSuggestion); // Check the state that NitzStateMachineImpl exposes for tests. assertEquals(initialNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); // Simulate the passage of time and update the device realtime clock. scenario.incrementTime(timeStepMillis); script.incrementTime(timeStepMillis); // Simulate the device receiving another NITZ signal. NitzSignal finalNitzSignal = scenario.createNitzSignal(mFakeDeviceState.elapsedRealtimeMillis(), ARBITRARY_AGE); script.nitzReceived(finalNitzSignal); // Verify the state machine did the right thing. TelephonyTimeSuggestion expectedFinalTimeSuggestion = createTimeSuggestionFromNitzSignal(SLOT_INDEX, finalNitzSignal); TelephonyTimeZoneSuggestion expectedFinalTimeZoneSuggestion = mRealTimeZoneSuggester.getTimeZoneSuggestion( SLOT_INDEX, countryIsoCode, finalNitzSignal); script.verifyTimeAndTimeZoneSuggestedAndReset( expectedFinalTimeSuggestion, expectedFinalTimeZoneSuggestion); // Check state that NitzStateMachineImpl exposes for tests. assertEquals(finalNitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); } Loading Loading @@ -477,7 +571,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { script.verifyTimeAndTimeZoneSuggestedAndReset( expectedTimeSuggestion, expectedTimeZoneSuggestion2); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertEquals(nitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); // Simulate the country becoming unavailable and verify the state machine does the right Loading @@ -488,7 +582,7 @@ public class NitzStateMachineImplTest extends TelephonyTest { SLOT_INDEX, null /* countryIsoCode */, nitzSignal); script.verifyOnlyTimeZoneWasSuggestedAndReset(expectedTimeZoneSuggestion3); // Check state that NitzStateMachine must expose. // Check state that NitzStateMachineImpl exposes for tests. assertEquals(nitzSignal.getNitzData(), mNitzStateMachineImpl.getCachedNitzData()); } Loading
tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineTestSupport.java +12 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes