Loading src/java/com/android/internal/telephony/NitzStateMachine.java +42 −75 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import android.telephony.Rlog; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.LocalLog; import android.util.TimeUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult; Loading @@ -36,7 +35,6 @@ import com.android.internal.util.IndentingPrintWriter; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.TimeZone; /** * {@hide} Loading Loading @@ -241,67 +239,17 @@ public class NitzStateMachine { + " not setting zone"); } } else { // mLatestNitzSignal != null if (nitzOffsetMightBeBogus(mLatestNitzSignal.mValue) && isTimeZoneSettingInitialized && !countryUsesUtc(isoCountryCode, mLatestNitzSignal)) { // This case means that (1) the device received an NITZ signal that could be // bogus due to having a zero offset from UTC, (2) the device has had a time // zone set explicitly and (3) the iso tells us the country is NOT one that uses // a zero offset. This is interpreted as being NITZ incorrectly reporting a // local time and not a UTC time. The zone is left as the current device's zone // setting, and the system clock may be adjusted by taking the NITZ time and // assuming the current zone setting is correct. TimeZone zone = TimeZone.getDefault(); if (isNitzSignalOffsetInfoBogus(mLatestNitzSignal, isoCountryCode)) { String logMsg = "handleNetworkCountryCodeSet: Stored NITZ looks bogus, " + " isoCountryCode=" + isoCountryCode + " mLatestNitzSignal=" + mLatestNitzSignal; if (DBG) { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: NITZ looks bogus, maybe using" + " current default zone to adjust the system clock," + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz + " mLatestNitzSignal=" + mLatestNitzSignal + " zone=" + zone); Rlog.d(LOG_TAG, logMsg); } zoneId = zone.getID(); if (mNeedCountryCodeForNitz) { NitzData nitzData = mLatestNitzSignal.mValue; try { // Acquire the wakelock as we're reading the elapsed realtime clock // here. mWakeLock.acquire(); mTimeZoneLog.log(logMsg); // Use the time that came with the NITZ offset that we think is bogus: // we just interpret it as local time. long ctm = nitzData.getCurrentTimeInMillis(); long delayAdjustedCtm = ctm + (mTimeServiceHelper.elapsedRealtime() - mLatestNitzSignal.mElapsedRealtime); long tzOffset = zone.getOffset(delayAdjustedCtm); if (DBG) { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet:" + " tzOffset=" + tzOffset + " delayAdjustedCtm=" + TimeUtils.logTimeOfDay(delayAdjustedCtm)); } if (mTimeServiceHelper.isTimeDetectionEnabled()) { long timeZoneAdjustedCtm = delayAdjustedCtm - tzOffset; String msg = "handleNetworkCountryCodeSet: setting time" + " timeZoneAdjustedCtm=" + TimeUtils.logTimeOfDay(timeZoneAdjustedCtm); setAndBroadcastNetworkSetTime(msg, timeZoneAdjustedCtm); } else { // Adjust the saved NITZ time to account for tzOffset. mSavedNitzTime = new TimeStampedValue<>( mSavedNitzTime.mValue - tzOffset, mSavedNitzTime.mElapsedRealtime); if (DBG) { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet:" + "adjusting time mSavedNitzTime=" + mSavedNitzTime); } } } finally { mWakeLock.release(); } } // No zone can be determined. zoneId = null; } else { NitzData nitzData = mLatestNitzSignal.mValue; OffsetResult lookupResult = Loading Loading @@ -372,14 +320,6 @@ public class NitzStateMachine { mNitzTimeZoneDetectionSuccessful = false; } /** * Returns {@code true} if the NITZ data looks like it might be incomplete or bogus, i.e. it has * a zero offset from UTC with either no DST information available or a zero DST offset. */ private static boolean nitzOffsetMightBeBogus(NitzData nitzData) { return nitzData.getLocalOffsetMillis() == 0 && !nitzData.isDst(); } /** * Handle a new NITZ signal being received. */ Loading @@ -391,6 +331,22 @@ public class NitzStateMachine { handleTimeFromNitz(nitzSignal); } /** * Returns true if the NITZ signal is definitely bogus, assuming that the country is correct. */ private boolean isNitzSignalOffsetInfoBogus( TimeStampedValue<NitzData> nitzSignal, String isoCountryCode) { if (TextUtils.isEmpty(isoCountryCode)) { // We cannot say for sure. return false; } NitzData newNitzData = nitzSignal.mValue; boolean zeroOffsetNitz = newNitzData.getLocalOffsetMillis() == 0 && !newNitzData.isDst(); return zeroOffsetNitz && !countryUsesUtc(isoCountryCode, nitzSignal); } private void handleTimeZoneFromNitz(TimeStampedValue<NitzData> nitzSignal) { try { NitzData newNitzData = nitzSignal.mValue; Loading @@ -406,12 +362,7 @@ public class NitzStateMachine { // We don't have a country code so we won't try to look up the time zone. zoneId = null; needCountryCode = true; } else if (!TextUtils.isEmpty(isoCountryCode)) { OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitzCountry( newNitzData, isoCountryCode); zoneId = lookupResult != null ? lookupResult.zoneId : null; needCountryCode = false; } else { } else if (TextUtils.isEmpty(isoCountryCode)) { // We have a country code but it's empty. This is most likely because we're on a // test network that's using a bogus MCC (eg, "001"), so get a TimeZone // based only on the NITZ parameters. Loading @@ -422,6 +373,22 @@ public class NitzStateMachine { } zoneId = lookupResult != null ? lookupResult.zoneId : null; needCountryCode = false; } else if (isNitzSignalOffsetInfoBogus(nitzSignal, isoCountryCode)) { String logMsg = "handleNitzReceived: Received NITZ looks bogus, " + " isoCountryCode=" + isoCountryCode + " nitzSignal=" + nitzSignal; if (DBG) { Rlog.d(LOG_TAG, logMsg); } mTimeZoneLog.log(logMsg); zoneId = null; needCountryCode = false; } else { OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitzCountry( newNitzData, isoCountryCode); zoneId = lookupResult != null ? lookupResult.zoneId : null; needCountryCode = false; } } Loading tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTest.java +274 −22 Original line number Diff line number Diff line Loading @@ -45,6 +45,37 @@ import org.mockito.Mock; public class NitzStateMachineTest extends TelephonyTest { // A country with a single zone : the zone can be guessed from the country. // The UK uses UTC for part of the year so it is not good for detecting bogus NITZ signals. private static final Scenario UNITED_KINGDOM_SCENARIO = new Scenario.Builder() .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0) .setInitialDeviceRealtimeMillis(123456789L) .setTimeZone("Europe/London") .setActualTimeUtc(2018, 1, 1, 12, 0, 0) .setCountryIso("gb") .build(); // A country that has multiple zones, but there is only one matching time zone at the time : // the zone cannot be guessed from the country alone, but can be guessed from the country + // NITZ. The US never uses UTC so it can be used for testing bogus NITZ signal handling. private static final Scenario UNIQUE_US_ZONE_SCENARIO = new Scenario.Builder() .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0) .setInitialDeviceRealtimeMillis(123456789L) .setTimeZone("America/Los_Angeles") .setActualTimeUtc(2018, 1, 1, 12, 0, 0) .setCountryIso("us") .build(); // A country with a single zone: the zone can be guessed from the country alone. CZ never uses // UTC so it can be used for testing bogus NITZ signal handling. private static final Scenario CZECHIA_SCENARIO = new Scenario.Builder() .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0) .setInitialDeviceRealtimeMillis(123456789L) .setTimeZone("Europe/Prague") .setActualTimeUtc(2018, 1, 1, 12, 0, 0) .setCountryIso("cz") .build(); @Mock private NitzStateMachine.DeviceState mDeviceState; Loading Loading @@ -75,17 +106,6 @@ public class NitzStateMachineTest extends TelephonyTest { super.tearDown(); } // A country that has multiple zones, but there is only one matching time zone at the time : // the zone cannot be guessed from the country alone, but can be guessed from the country + // NITZ. private static final Scenario UNIQUE_US_ZONE_SCENARIO = new Scenario.Builder() .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0) .setInitialDeviceRealtimeMillis(123456789L) .setTimeZone("America/Los_Angeles") .setActualTimeUtc(2018, 1, 1, 12, 0, 0) .setCountryIso("us") .build(); @Test public void test_uniqueUsZone_Assumptions() { // Check we'll get the expected behavior from TimeZoneLookupHelper. Loading @@ -109,15 +129,6 @@ public class NitzStateMachineTest extends TelephonyTest { assertEquals(expectedLookupResult, actualLookupResult); } // A country with a single zone : the zone can be guessed from the country. private static final Scenario UNITED_KINGDOM_SCENARIO = new Scenario.Builder() .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0) .setInitialDeviceRealtimeMillis(123456789L) .setTimeZone("Europe/London") .setActualTimeUtc(2018, 1, 1, 12, 0, 0) .setCountryIso("gb") .build(); @Test public void test_unitedKingdom_Assumptions() { // Check we'll get the expected behavior from TimeZoneLookupHelper. Loading Loading @@ -416,6 +427,246 @@ public class NitzStateMachineTest extends TelephonyTest { assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); } @Test public void test_validCzNitzSignal_nitzReceivedFirst() throws Exception { Scenario scenario = CZECHIA_SCENARIO; Device device = new DeviceBuilder() .setClocksFromScenario(scenario) .setTimeDetectionEnabled(true) .setTimeZoneDetectionEnabled(true) .setTimeZoneSettingInitialized(true) .initialize(); Script script = new Script(device); TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal(); // Simulate receiving an NITZ signal. script.nitzReceived(goodNitzSignal) // The NITZ alone isn't enough to detect a time zone. .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis()); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(goodNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertNull(mNitzStateMachine.getSavedTimeZoneId()); // Simulate the country code becoming known. script.countryReceived(scenario.getNetworkCountryIsoCode()) // The NITZ country is enough to detect the time zone, but the NITZ + country is // also sufficient so we expect the time zone to be set twice. .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2); // Check NitzStateMachine state. assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(goodNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); } @Test public void test_validCzNitzSignal_countryReceivedFirst() throws Exception { Scenario scenario = CZECHIA_SCENARIO; Device device = new DeviceBuilder() .setClocksFromScenario(scenario) .setTimeDetectionEnabled(true) .setTimeZoneDetectionEnabled(true) .setTimeZoneSettingInitialized(true) .initialize(); Script script = new Script(device); TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal(); // Simulate the country code becoming known. script.countryReceived(scenario.getNetworkCountryIsoCode()) // The NITZ country is enough to detect the time zone. .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertNull(mNitzStateMachine.getCachedNitzData()); assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); // Simulate receiving an NITZ signal. script.nitzReceived(goodNitzSignal) // The time will be set from the NITZ signal. // The combination of NITZ + country will cause the time zone to be set. .verifyTimeAndZoneSetAndReset( scenario.getActualTimeMillis(), scenario.getTimeZoneId()); // Check NitzStateMachine state. assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(goodNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); } @Test public void test_bogusCzNitzSignal_nitzReceivedFirst() throws Exception { Scenario scenario = CZECHIA_SCENARIO; Device device = new DeviceBuilder() .setClocksFromScenario(scenario) .setTimeDetectionEnabled(true) .setTimeZoneDetectionEnabled(true) .setTimeZoneSettingInitialized(true) .initialize(); Script script = new Script(device); TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal(); // Create a corrupted NITZ signal, where the offset information has been lost. NitzData bogusNitzData = NitzData.createForTests( 0 /* UTC! */, null /* dstOffsetMillis */, goodNitzSignal.mValue.getCurrentTimeInMillis(), null /* emulatorHostTimeZone */); TimeStampedValue<NitzData> badNitzSignal = new TimeStampedValue<>( bogusNitzData, goodNitzSignal.mElapsedRealtime); // Simulate receiving an NITZ signal. script.nitzReceived(badNitzSignal) // The NITZ alone isn't enough to detect a time zone, but there isn't enough // information to work out its bogus. .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis()); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertNull(mNitzStateMachine.getSavedTimeZoneId()); // Simulate the country code becoming known. script.countryReceived(scenario.getNetworkCountryIsoCode()) // The country is enough to detect the time zone for CZ. If the NITZ signal // wasn't obviously bogus we'd try to set it twice. .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); } @Test public void test_bogusCzNitzSignal_countryReceivedFirst() throws Exception { Scenario scenario = CZECHIA_SCENARIO; Device device = new DeviceBuilder() .setClocksFromScenario(scenario) .setTimeDetectionEnabled(true) .setTimeZoneDetectionEnabled(true) .setTimeZoneSettingInitialized(true) .initialize(); Script script = new Script(device); TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal(); // Create a corrupted NITZ signal, where the offset information has been lost. NitzData bogusNitzData = NitzData.createForTests( 0 /* UTC! */, null /* dstOffsetMillis */, goodNitzSignal.mValue.getCurrentTimeInMillis(), null /* emulatorHostTimeZone */); TimeStampedValue<NitzData> badNitzSignal = new TimeStampedValue<>( bogusNitzData, goodNitzSignal.mElapsedRealtime); // Simulate the country code becoming known. script.countryReceived(scenario.getNetworkCountryIsoCode()) // The country is enough to detect the time zone for CZ. .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId()); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertNull(mNitzStateMachine.getCachedNitzData()); assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); // Simulate receiving an NITZ signal. script.nitzReceived(badNitzSignal) // The NITZ should be detected as bogus so only the time will be set. .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis()); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); } @Test public void test_bogusUniqueUsNitzSignal_nitzReceivedFirst() throws Exception { Scenario scenario = UNIQUE_US_ZONE_SCENARIO; Device device = new DeviceBuilder() .setClocksFromScenario(scenario) .setTimeDetectionEnabled(true) .setTimeZoneDetectionEnabled(true) .setTimeZoneSettingInitialized(true) .initialize(); Script script = new Script(device); TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal(); // Create a corrupted NITZ signal, where the offset information has been lost. NitzData bogusNitzData = NitzData.createForTests( 0 /* UTC! */, null /* dstOffsetMillis */, goodNitzSignal.mValue.getCurrentTimeInMillis(), null /* emulatorHostTimeZone */); TimeStampedValue<NitzData> badNitzSignal = new TimeStampedValue<>( bogusNitzData, goodNitzSignal.mElapsedRealtime); // Simulate receiving an NITZ signal. script.nitzReceived(badNitzSignal) // The NITZ alone isn't enough to detect a time zone, but there isn't enough // information to work out its bogus. .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis()); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertNull(mNitzStateMachine.getSavedTimeZoneId()); // Simulate the country code becoming known. script.countryReceived(scenario.getNetworkCountryIsoCode()) // The country isn't enough to detect the time zone for US so we will leave the time // zone unset. .verifyNothingWasSetAndReset(); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertNull(mNitzStateMachine.getSavedTimeZoneId()); } @Test public void test_bogusUsUniqueNitzSignal_countryReceivedFirst() throws Exception { Scenario scenario = UNIQUE_US_ZONE_SCENARIO; Device device = new DeviceBuilder() .setClocksFromScenario(scenario) .setTimeDetectionEnabled(true) .setTimeZoneDetectionEnabled(true) .setTimeZoneSettingInitialized(true) .initialize(); Script script = new Script(device); TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal(); // Create a corrupted NITZ signal, where the offset information has been lost. NitzData bogusNitzData = NitzData.createForTests( 0 /* UTC! */, null /* dstOffsetMillis */, goodNitzSignal.mValue.getCurrentTimeInMillis(), null /* emulatorHostTimeZone */); TimeStampedValue<NitzData> badNitzSignal = new TimeStampedValue<>( bogusNitzData, goodNitzSignal.mElapsedRealtime); // Simulate the country code becoming known. script.countryReceived(scenario.getNetworkCountryIsoCode()) // The country isn't enough to detect the time zone for US so we will leave the time // zone unset. .verifyNothingWasSetAndReset(); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertNull(mNitzStateMachine.getCachedNitzData()); assertNull(mNitzStateMachine.getSavedTimeZoneId()); // Simulate receiving an NITZ signal. script.nitzReceived(badNitzSignal) // The NITZ should be detected as bogus so only the time will be set. .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis()); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertNull(mNitzStateMachine.getSavedTimeZoneId()); } private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute, int second) { Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC")); Loading Loading @@ -647,8 +898,9 @@ public class NitzStateMachineTest extends TelephonyTest { int[] offsets = new int[2]; mZone.getOffset(mActualTimeMillis, false /* local */, offsets); int zoneOffsetMillis = offsets[0] + offsets[1]; NitzData nitzData = NitzData .createForTests(zoneOffsetMillis, offsets[1], mActualTimeMillis, null); NitzData nitzData = NitzData.createForTests( zoneOffsetMillis, offsets[1], mActualTimeMillis, null /* emulatorHostTimeZone */); mNitzSignal = new TimeStampedValue<>(nitzData, mInitialDeviceRealtimeMillis); } return mNitzSignal; Loading Loading
src/java/com/android/internal/telephony/NitzStateMachine.java +42 −75 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import android.telephony.Rlog; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.LocalLog; import android.util.TimeUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult; Loading @@ -36,7 +35,6 @@ import com.android.internal.util.IndentingPrintWriter; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.TimeZone; /** * {@hide} Loading Loading @@ -241,67 +239,17 @@ public class NitzStateMachine { + " not setting zone"); } } else { // mLatestNitzSignal != null if (nitzOffsetMightBeBogus(mLatestNitzSignal.mValue) && isTimeZoneSettingInitialized && !countryUsesUtc(isoCountryCode, mLatestNitzSignal)) { // This case means that (1) the device received an NITZ signal that could be // bogus due to having a zero offset from UTC, (2) the device has had a time // zone set explicitly and (3) the iso tells us the country is NOT one that uses // a zero offset. This is interpreted as being NITZ incorrectly reporting a // local time and not a UTC time. The zone is left as the current device's zone // setting, and the system clock may be adjusted by taking the NITZ time and // assuming the current zone setting is correct. TimeZone zone = TimeZone.getDefault(); if (isNitzSignalOffsetInfoBogus(mLatestNitzSignal, isoCountryCode)) { String logMsg = "handleNetworkCountryCodeSet: Stored NITZ looks bogus, " + " isoCountryCode=" + isoCountryCode + " mLatestNitzSignal=" + mLatestNitzSignal; if (DBG) { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: NITZ looks bogus, maybe using" + " current default zone to adjust the system clock," + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz + " mLatestNitzSignal=" + mLatestNitzSignal + " zone=" + zone); Rlog.d(LOG_TAG, logMsg); } zoneId = zone.getID(); if (mNeedCountryCodeForNitz) { NitzData nitzData = mLatestNitzSignal.mValue; try { // Acquire the wakelock as we're reading the elapsed realtime clock // here. mWakeLock.acquire(); mTimeZoneLog.log(logMsg); // Use the time that came with the NITZ offset that we think is bogus: // we just interpret it as local time. long ctm = nitzData.getCurrentTimeInMillis(); long delayAdjustedCtm = ctm + (mTimeServiceHelper.elapsedRealtime() - mLatestNitzSignal.mElapsedRealtime); long tzOffset = zone.getOffset(delayAdjustedCtm); if (DBG) { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet:" + " tzOffset=" + tzOffset + " delayAdjustedCtm=" + TimeUtils.logTimeOfDay(delayAdjustedCtm)); } if (mTimeServiceHelper.isTimeDetectionEnabled()) { long timeZoneAdjustedCtm = delayAdjustedCtm - tzOffset; String msg = "handleNetworkCountryCodeSet: setting time" + " timeZoneAdjustedCtm=" + TimeUtils.logTimeOfDay(timeZoneAdjustedCtm); setAndBroadcastNetworkSetTime(msg, timeZoneAdjustedCtm); } else { // Adjust the saved NITZ time to account for tzOffset. mSavedNitzTime = new TimeStampedValue<>( mSavedNitzTime.mValue - tzOffset, mSavedNitzTime.mElapsedRealtime); if (DBG) { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet:" + "adjusting time mSavedNitzTime=" + mSavedNitzTime); } } } finally { mWakeLock.release(); } } // No zone can be determined. zoneId = null; } else { NitzData nitzData = mLatestNitzSignal.mValue; OffsetResult lookupResult = Loading Loading @@ -372,14 +320,6 @@ public class NitzStateMachine { mNitzTimeZoneDetectionSuccessful = false; } /** * Returns {@code true} if the NITZ data looks like it might be incomplete or bogus, i.e. it has * a zero offset from UTC with either no DST information available or a zero DST offset. */ private static boolean nitzOffsetMightBeBogus(NitzData nitzData) { return nitzData.getLocalOffsetMillis() == 0 && !nitzData.isDst(); } /** * Handle a new NITZ signal being received. */ Loading @@ -391,6 +331,22 @@ public class NitzStateMachine { handleTimeFromNitz(nitzSignal); } /** * Returns true if the NITZ signal is definitely bogus, assuming that the country is correct. */ private boolean isNitzSignalOffsetInfoBogus( TimeStampedValue<NitzData> nitzSignal, String isoCountryCode) { if (TextUtils.isEmpty(isoCountryCode)) { // We cannot say for sure. return false; } NitzData newNitzData = nitzSignal.mValue; boolean zeroOffsetNitz = newNitzData.getLocalOffsetMillis() == 0 && !newNitzData.isDst(); return zeroOffsetNitz && !countryUsesUtc(isoCountryCode, nitzSignal); } private void handleTimeZoneFromNitz(TimeStampedValue<NitzData> nitzSignal) { try { NitzData newNitzData = nitzSignal.mValue; Loading @@ -406,12 +362,7 @@ public class NitzStateMachine { // We don't have a country code so we won't try to look up the time zone. zoneId = null; needCountryCode = true; } else if (!TextUtils.isEmpty(isoCountryCode)) { OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitzCountry( newNitzData, isoCountryCode); zoneId = lookupResult != null ? lookupResult.zoneId : null; needCountryCode = false; } else { } else if (TextUtils.isEmpty(isoCountryCode)) { // We have a country code but it's empty. This is most likely because we're on a // test network that's using a bogus MCC (eg, "001"), so get a TimeZone // based only on the NITZ parameters. Loading @@ -422,6 +373,22 @@ public class NitzStateMachine { } zoneId = lookupResult != null ? lookupResult.zoneId : null; needCountryCode = false; } else if (isNitzSignalOffsetInfoBogus(nitzSignal, isoCountryCode)) { String logMsg = "handleNitzReceived: Received NITZ looks bogus, " + " isoCountryCode=" + isoCountryCode + " nitzSignal=" + nitzSignal; if (DBG) { Rlog.d(LOG_TAG, logMsg); } mTimeZoneLog.log(logMsg); zoneId = null; needCountryCode = false; } else { OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitzCountry( newNitzData, isoCountryCode); zoneId = lookupResult != null ? lookupResult.zoneId : null; needCountryCode = false; } } Loading
tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTest.java +274 −22 Original line number Diff line number Diff line Loading @@ -45,6 +45,37 @@ import org.mockito.Mock; public class NitzStateMachineTest extends TelephonyTest { // A country with a single zone : the zone can be guessed from the country. // The UK uses UTC for part of the year so it is not good for detecting bogus NITZ signals. private static final Scenario UNITED_KINGDOM_SCENARIO = new Scenario.Builder() .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0) .setInitialDeviceRealtimeMillis(123456789L) .setTimeZone("Europe/London") .setActualTimeUtc(2018, 1, 1, 12, 0, 0) .setCountryIso("gb") .build(); // A country that has multiple zones, but there is only one matching time zone at the time : // the zone cannot be guessed from the country alone, but can be guessed from the country + // NITZ. The US never uses UTC so it can be used for testing bogus NITZ signal handling. private static final Scenario UNIQUE_US_ZONE_SCENARIO = new Scenario.Builder() .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0) .setInitialDeviceRealtimeMillis(123456789L) .setTimeZone("America/Los_Angeles") .setActualTimeUtc(2018, 1, 1, 12, 0, 0) .setCountryIso("us") .build(); // A country with a single zone: the zone can be guessed from the country alone. CZ never uses // UTC so it can be used for testing bogus NITZ signal handling. private static final Scenario CZECHIA_SCENARIO = new Scenario.Builder() .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0) .setInitialDeviceRealtimeMillis(123456789L) .setTimeZone("Europe/Prague") .setActualTimeUtc(2018, 1, 1, 12, 0, 0) .setCountryIso("cz") .build(); @Mock private NitzStateMachine.DeviceState mDeviceState; Loading Loading @@ -75,17 +106,6 @@ public class NitzStateMachineTest extends TelephonyTest { super.tearDown(); } // A country that has multiple zones, but there is only one matching time zone at the time : // the zone cannot be guessed from the country alone, but can be guessed from the country + // NITZ. private static final Scenario UNIQUE_US_ZONE_SCENARIO = new Scenario.Builder() .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0) .setInitialDeviceRealtimeMillis(123456789L) .setTimeZone("America/Los_Angeles") .setActualTimeUtc(2018, 1, 1, 12, 0, 0) .setCountryIso("us") .build(); @Test public void test_uniqueUsZone_Assumptions() { // Check we'll get the expected behavior from TimeZoneLookupHelper. Loading @@ -109,15 +129,6 @@ public class NitzStateMachineTest extends TelephonyTest { assertEquals(expectedLookupResult, actualLookupResult); } // A country with a single zone : the zone can be guessed from the country. private static final Scenario UNITED_KINGDOM_SCENARIO = new Scenario.Builder() .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0) .setInitialDeviceRealtimeMillis(123456789L) .setTimeZone("Europe/London") .setActualTimeUtc(2018, 1, 1, 12, 0, 0) .setCountryIso("gb") .build(); @Test public void test_unitedKingdom_Assumptions() { // Check we'll get the expected behavior from TimeZoneLookupHelper. Loading Loading @@ -416,6 +427,246 @@ public class NitzStateMachineTest extends TelephonyTest { assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); } @Test public void test_validCzNitzSignal_nitzReceivedFirst() throws Exception { Scenario scenario = CZECHIA_SCENARIO; Device device = new DeviceBuilder() .setClocksFromScenario(scenario) .setTimeDetectionEnabled(true) .setTimeZoneDetectionEnabled(true) .setTimeZoneSettingInitialized(true) .initialize(); Script script = new Script(device); TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal(); // Simulate receiving an NITZ signal. script.nitzReceived(goodNitzSignal) // The NITZ alone isn't enough to detect a time zone. .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis()); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(goodNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertNull(mNitzStateMachine.getSavedTimeZoneId()); // Simulate the country code becoming known. script.countryReceived(scenario.getNetworkCountryIsoCode()) // The NITZ country is enough to detect the time zone, but the NITZ + country is // also sufficient so we expect the time zone to be set twice. .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2); // Check NitzStateMachine state. assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(goodNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); } @Test public void test_validCzNitzSignal_countryReceivedFirst() throws Exception { Scenario scenario = CZECHIA_SCENARIO; Device device = new DeviceBuilder() .setClocksFromScenario(scenario) .setTimeDetectionEnabled(true) .setTimeZoneDetectionEnabled(true) .setTimeZoneSettingInitialized(true) .initialize(); Script script = new Script(device); TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal(); // Simulate the country code becoming known. script.countryReceived(scenario.getNetworkCountryIsoCode()) // The NITZ country is enough to detect the time zone. .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertNull(mNitzStateMachine.getCachedNitzData()); assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); // Simulate receiving an NITZ signal. script.nitzReceived(goodNitzSignal) // The time will be set from the NITZ signal. // The combination of NITZ + country will cause the time zone to be set. .verifyTimeAndZoneSetAndReset( scenario.getActualTimeMillis(), scenario.getTimeZoneId()); // Check NitzStateMachine state. assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(goodNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); } @Test public void test_bogusCzNitzSignal_nitzReceivedFirst() throws Exception { Scenario scenario = CZECHIA_SCENARIO; Device device = new DeviceBuilder() .setClocksFromScenario(scenario) .setTimeDetectionEnabled(true) .setTimeZoneDetectionEnabled(true) .setTimeZoneSettingInitialized(true) .initialize(); Script script = new Script(device); TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal(); // Create a corrupted NITZ signal, where the offset information has been lost. NitzData bogusNitzData = NitzData.createForTests( 0 /* UTC! */, null /* dstOffsetMillis */, goodNitzSignal.mValue.getCurrentTimeInMillis(), null /* emulatorHostTimeZone */); TimeStampedValue<NitzData> badNitzSignal = new TimeStampedValue<>( bogusNitzData, goodNitzSignal.mElapsedRealtime); // Simulate receiving an NITZ signal. script.nitzReceived(badNitzSignal) // The NITZ alone isn't enough to detect a time zone, but there isn't enough // information to work out its bogus. .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis()); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertNull(mNitzStateMachine.getSavedTimeZoneId()); // Simulate the country code becoming known. script.countryReceived(scenario.getNetworkCountryIsoCode()) // The country is enough to detect the time zone for CZ. If the NITZ signal // wasn't obviously bogus we'd try to set it twice. .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); } @Test public void test_bogusCzNitzSignal_countryReceivedFirst() throws Exception { Scenario scenario = CZECHIA_SCENARIO; Device device = new DeviceBuilder() .setClocksFromScenario(scenario) .setTimeDetectionEnabled(true) .setTimeZoneDetectionEnabled(true) .setTimeZoneSettingInitialized(true) .initialize(); Script script = new Script(device); TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal(); // Create a corrupted NITZ signal, where the offset information has been lost. NitzData bogusNitzData = NitzData.createForTests( 0 /* UTC! */, null /* dstOffsetMillis */, goodNitzSignal.mValue.getCurrentTimeInMillis(), null /* emulatorHostTimeZone */); TimeStampedValue<NitzData> badNitzSignal = new TimeStampedValue<>( bogusNitzData, goodNitzSignal.mElapsedRealtime); // Simulate the country code becoming known. script.countryReceived(scenario.getNetworkCountryIsoCode()) // The country is enough to detect the time zone for CZ. .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId()); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertNull(mNitzStateMachine.getCachedNitzData()); assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); // Simulate receiving an NITZ signal. script.nitzReceived(badNitzSignal) // The NITZ should be detected as bogus so only the time will be set. .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis()); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId()); } @Test public void test_bogusUniqueUsNitzSignal_nitzReceivedFirst() throws Exception { Scenario scenario = UNIQUE_US_ZONE_SCENARIO; Device device = new DeviceBuilder() .setClocksFromScenario(scenario) .setTimeDetectionEnabled(true) .setTimeZoneDetectionEnabled(true) .setTimeZoneSettingInitialized(true) .initialize(); Script script = new Script(device); TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal(); // Create a corrupted NITZ signal, where the offset information has been lost. NitzData bogusNitzData = NitzData.createForTests( 0 /* UTC! */, null /* dstOffsetMillis */, goodNitzSignal.mValue.getCurrentTimeInMillis(), null /* emulatorHostTimeZone */); TimeStampedValue<NitzData> badNitzSignal = new TimeStampedValue<>( bogusNitzData, goodNitzSignal.mElapsedRealtime); // Simulate receiving an NITZ signal. script.nitzReceived(badNitzSignal) // The NITZ alone isn't enough to detect a time zone, but there isn't enough // information to work out its bogus. .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis()); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertNull(mNitzStateMachine.getSavedTimeZoneId()); // Simulate the country code becoming known. script.countryReceived(scenario.getNetworkCountryIsoCode()) // The country isn't enough to detect the time zone for US so we will leave the time // zone unset. .verifyNothingWasSetAndReset(); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertNull(mNitzStateMachine.getSavedTimeZoneId()); } @Test public void test_bogusUsUniqueNitzSignal_countryReceivedFirst() throws Exception { Scenario scenario = UNIQUE_US_ZONE_SCENARIO; Device device = new DeviceBuilder() .setClocksFromScenario(scenario) .setTimeDetectionEnabled(true) .setTimeZoneDetectionEnabled(true) .setTimeZoneSettingInitialized(true) .initialize(); Script script = new Script(device); TimeStampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal(); // Create a corrupted NITZ signal, where the offset information has been lost. NitzData bogusNitzData = NitzData.createForTests( 0 /* UTC! */, null /* dstOffsetMillis */, goodNitzSignal.mValue.getCurrentTimeInMillis(), null /* emulatorHostTimeZone */); TimeStampedValue<NitzData> badNitzSignal = new TimeStampedValue<>( bogusNitzData, goodNitzSignal.mElapsedRealtime); // Simulate the country code becoming known. script.countryReceived(scenario.getNetworkCountryIsoCode()) // The country isn't enough to detect the time zone for US so we will leave the time // zone unset. .verifyNothingWasSetAndReset(); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertNull(mNitzStateMachine.getCachedNitzData()); assertNull(mNitzStateMachine.getSavedTimeZoneId()); // Simulate receiving an NITZ signal. script.nitzReceived(badNitzSignal) // The NITZ should be detected as bogus so only the time will be set. .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis()); // Check NitzStateMachine state. assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful()); assertEquals(badNitzSignal.mValue, mNitzStateMachine.getCachedNitzData()); assertNull(mNitzStateMachine.getSavedTimeZoneId()); } private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute, int second) { Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC")); Loading Loading @@ -647,8 +898,9 @@ public class NitzStateMachineTest extends TelephonyTest { int[] offsets = new int[2]; mZone.getOffset(mActualTimeMillis, false /* local */, offsets); int zoneOffsetMillis = offsets[0] + offsets[1]; NitzData nitzData = NitzData .createForTests(zoneOffsetMillis, offsets[1], mActualTimeMillis, null); NitzData nitzData = NitzData.createForTests( zoneOffsetMillis, offsets[1], mActualTimeMillis, null /* emulatorHostTimeZone */); mNitzSignal = new TimeStampedValue<>(nitzData, mInitialDeviceRealtimeMillis); } return mNitzSignal; Loading