Loading src/java/com/android/internal/telephony/NitzStateMachine.java +55 −43 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import android.util.LocalLog; import android.util.TimeUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult; import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.util.TimeStampedValue; import com.android.internal.util.IndentingPrintWriter; Loading Loading @@ -165,7 +167,7 @@ public class NitzStateMachine { public NitzStateMachine(GsmCdmaPhone phone) { this(phone, TelephonyComponentFactory.getInstance().makeTimeServiceHelper(phone.getContext()), new TimeServiceHelper(phone.getContext()), new DeviceState(phone), new TimeZoneLookupHelper()); } Loading Loading @@ -218,10 +220,10 @@ public class NitzStateMachine { } if (countryChanged || mNeedCountryCodeForNitz) { // Capture the time zone property. This allows us to tell whether the device has a time // zone set. TimeZone.getDefault() returns a default zone (GMT) even when time zone is // not explicitly set making the system property a better indicator of whether an // explicit time zone choice has been made. // TimeZone.getDefault() returns a default zone (GMT) even when time zone have never // been set which makes it difficult to tell if it's what the user / time zone detection // has chosen. isTimeZoneSettingInitialized() tells us whether the time zone of the // device has ever been explicit set by the user or code. final boolean isTimeZoneSettingInitialized = mTimeServiceHelper.isTimeZoneSettingInitialized(); if (DBG) { Loading @@ -237,11 +239,13 @@ public class NitzStateMachine { // mNeedCountryCodeForNitz is only set to true when mLatestNitzSignal is set so // there's no need to check mLatestNitzSignal == null. zoneId = mTimeZoneLookupHelper.guessZoneIdByNitz(mLatestNitzSignal.mValue); OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitz(mLatestNitzSignal.mValue); if (DBG) { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: guessZoneIdByNitz() returned" + " zoneId=" + zoneId); + " lookupResult=" + lookupResult); } zoneId = lookupResult != null ? lookupResult.zoneId : null; } else if (mLatestNitzSignal == null) { zoneId = null; if (DBG) { Loading @@ -254,13 +258,21 @@ public class NitzStateMachine { && !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 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 // 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 (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); } zoneId = zone.getID(); if (mNeedCountryCodeForNitz) { Loading Loading @@ -302,25 +314,24 @@ public class NitzStateMachine { mWakeLock.release(); } } if (DBG) { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: using default TimeZone"); } } else { NitzData nitzData = mLatestNitzSignal.mValue; zoneId = mTimeZoneLookupHelper.guessZoneIdByNitzCountry( nitzData, isoCountryCode); OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, isoCountryCode); if (DBG) { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: using" + " guessZoneIdByNitzCountry(nitzData, isoCountryCode)," + " nitzData=" + nitzData + " isoCountryCode=" + isoCountryCode); + " isoCountryCode=" + isoCountryCode + " lookupResult=" + lookupResult); } zoneId = lookupResult != null ? lookupResult.zoneId : null; } } final String tmpLog = "handleNetworkCountryCodeSet:" + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized + " mLatestNitzSignal=" + mLatestNitzSignal + " iso-cc=" + isoCountryCode + " isoCountryCode=" + isoCountryCode + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz + " zoneId=" + zoneId; mTimeZoneLog.log(tmpLog); Loading @@ -334,10 +345,10 @@ public class NitzStateMachine { + " isTimeZoneDetectionEnabled() is false"); } if (mNeedCountryCodeForNitz) { saveNitzTimeZone(zoneId); mSavedTimeZoneId = zoneId; } } else { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: zoneId == null, do nothing"); Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: lookupResult == null, do nothing"); } mNeedCountryCodeForNitz = false; } Loading Loading @@ -401,17 +412,20 @@ public class NitzStateMachine { if (!mGotCountryCode) { zoneId = null; } else if (!TextUtils.isEmpty(iso)) { zoneId = mTimeZoneLookupHelper.guessZoneIdByNitzCountry(newNitzData, iso); OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitzCountry(newNitzData, iso); zoneId = lookupResult != null ? lookupResult.zoneId : null; } else { // We don't have a valid iso country code. 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. zoneId = mTimeZoneLookupHelper.guessZoneIdByNitz(newNitzData); OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitz(newNitzData); if (DBG) { Rlog.d(LOG_TAG, "handleTimeZoneFromNitz: guessZoneIdByNitz returned" + " zoneId=" + zoneId); + " lookupResult=" + lookupResult); } zoneId = lookupResult != null ? lookupResult.zoneId : null; } } Loading @@ -425,7 +439,7 @@ public class NitzStateMachine { mLatestNitzSignal = nitzSignal; } String tmpLog = "handleTimeZoneFromNitz: nitzTimeSignal=" + nitzSignal String tmpLog = "handleTimeZoneFromNitz: nitzSignal=" + nitzSignal + " zoneId=" + zoneId + " iso=" + iso + " mGotCountryCode=" + mGotCountryCode + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz Loading @@ -441,7 +455,7 @@ public class NitzStateMachine { setAndBroadcastNetworkSetTimeZone(zoneId); } mNitzTimeZoneDetectionSuccessful = true; saveNitzTimeZone(zoneId); mSavedTimeZoneId = zoneId; } } catch (RuntimeException ex) { Rlog.e(LOG_TAG, "handleTimeZoneFromNitz: Processing NITZ data" Loading @@ -455,7 +469,7 @@ public class NitzStateMachine { || one.isDst() != two.isDst(); } private void handleTimeFromNitz(TimeStampedValue<NitzData> nitzTimeSignal) { private void handleTimeFromNitz(TimeStampedValue<NitzData> nitzSignal) { try { boolean ignoreNitz = mDeviceState.getIgnoreNitz(); if (ignoreNitz) { Loading @@ -471,24 +485,24 @@ public class NitzStateMachine { // Validate the nitzTimeSignal to reject obviously bogus elapsedRealtime values. long elapsedRealtime = mDeviceState.elapsedRealtime(); long millisSinceNitzReceived = elapsedRealtime - nitzTimeSignal.mElapsedRealtime; long millisSinceNitzReceived = elapsedRealtime - nitzSignal.mElapsedRealtime; if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) { if (DBG) { Rlog.d(LOG_TAG, "handleTimeFromNitz: not setting time, unexpected" + " elapsedRealtime=" + elapsedRealtime + " nitzTimeSignal=" + nitzTimeSignal); + " nitzSignal=" + nitzSignal); } return; } // Adjust the NITZ time by the delay since it was received to get the time now. long adjustedCurrentTimeMillis = nitzTimeSignal.mValue.getCurrentTimeInMillis() + millisSinceNitzReceived; nitzSignal.mValue.getCurrentTimeInMillis() + millisSinceNitzReceived; long gained = adjustedCurrentTimeMillis - mDeviceState.currentTimeMillis(); if (mTimeServiceHelper.isTimeDetectionEnabled()) { String logMsg = "handleTimeFromNitz:" + " nitzTimeSignal=" + nitzTimeSignal + " nitzSignal=" + nitzSignal + " adjustedCurrentTimeMillis=" + adjustedCurrentTimeMillis + " millisSinceNitzReceived= " + millisSinceNitzReceived + " gained=" + gained; Loading Loading @@ -526,24 +540,20 @@ public class NitzStateMachine { // Save the last NITZ time signal used so we can return to it later // if auto-time detection is toggled. mSavedNitzTime = new TimeStampedValue<>( adjustedCurrentTimeMillis, nitzTimeSignal.mElapsedRealtime); adjustedCurrentTimeMillis, nitzSignal.mElapsedRealtime); } finally { mWakeLock.release(); } } catch (RuntimeException ex) { Rlog.e(LOG_TAG, "handleTimeFromNitz: Processing NITZ data" + " nitzTimeSignal=" + nitzTimeSignal + " nitzSignal=" + nitzSignal + " ex=" + ex); } } private void saveNitzTimeZone(String zoneId) { mSavedTimeZoneId = zoneId; } private void setAndBroadcastNetworkSetTimeZone(String zoneId) { if (DBG) { Rlog.d(LOG_TAG, "setAndBroadcastNetworkSetTimeZone: setTimeZone=" + zoneId); Rlog.d(LOG_TAG, "setAndBroadcastNetworkSetTimeZone: zoneId=" + zoneId); } mTimeServiceHelper.setDeviceTimeZone(zoneId); if (DBG) { Loading Loading @@ -619,7 +629,7 @@ public class NitzStateMachine { pw.println(" mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz); pw.println(" mLatestNitzSignal=" + mLatestNitzSignal); pw.println(" mGotCountryCode=" + mGotCountryCode); pw.println(" mSavedTimeZone=" + mSavedTimeZoneId); pw.println(" mSavedTimeZoneId=" + mSavedTimeZoneId); pw.println(" mNitzTimeZoneDetectionSuccessful=" + mNitzTimeZoneDetectionSuccessful); // Miscellaneous Loading Loading @@ -649,20 +659,22 @@ public class NitzStateMachine { * @param iso Country code from network MCC */ private void updateTimeZoneByNetworkCountryCode(String iso) { String zoneId = mTimeZoneLookupHelper.guessZoneIdByCountry( CountryResult lookupResult = mTimeZoneLookupHelper.lookupByCountry( iso, mDeviceState.currentTimeMillis()); if (zoneId != null) { if (lookupResult != null && lookupResult.allZonesHaveSameOffset) { String logMsg = "updateTimeZoneByNetworkCountryCode: set time" + " zoneId=" + zoneId + " lookupResult=" + lookupResult + " iso=" + iso; if (DBG) { Rlog.d(LOG_TAG, logMsg); } mTimeZoneLog.log(logMsg); setAndBroadcastNetworkSetTimeZone(zoneId); setAndBroadcastNetworkSetTimeZone(lookupResult.zoneId); } else { if (DBG) { Rlog.d(LOG_TAG, "updateTimeZoneByNetworkCountryCode: no good zone for iso=" + iso); Rlog.d(LOG_TAG, "updateTimeZoneByNetworkCountryCode: no good zone for" + " iso=" + iso + " lookupResult=" + lookupResult); } } } Loading src/java/com/android/internal/telephony/TelephonyComponentFactory.java +0 −7 Original line number Diff line number Diff line Loading @@ -68,13 +68,6 @@ public class TelephonyComponentFactory { return new NitzStateMachine(phone); } /** * Returns a new {@link TimeServiceHelper} instance. */ public TimeServiceHelper makeTimeServiceHelper(Context context) { return new TimeServiceHelper(context); } public SimActivationTracker makeSimActivationTracker(Phone phone) { return new SimActivationTracker(phone); } Loading src/java/com/android/internal/telephony/TimeZoneLookupHelper.java +172 −47 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ package com.android.internal.telephony; import android.text.TextUtils; import android.util.TimeUtils; import libcore.util.CountryTimeZones; import libcore.util.TimeZoneFinder; Loading @@ -30,43 +29,148 @@ import java.util.TimeZone; */ // Non-final to allow mocking. public class TimeZoneLookupHelper { private static final int MS_PER_HOUR = 60 * 60 * 1000; public TimeZoneLookupHelper() {} /** * The result of looking up a time zone using offset information (and possibly more). */ public static final class OffsetResult { /** A zone that matches the supplied criteria. See also {@link #isOnlyMatch}. */ public final String zoneId; /** True if there is only one matching time zone for the supplied criteria. */ public final boolean isOnlyMatch; public OffsetResult(String zoneId, boolean isOnlyMatch) { this.zoneId = zoneId; this.isOnlyMatch = isOnlyMatch; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } OffsetResult result = (OffsetResult) o; if (isOnlyMatch != result.isOnlyMatch) { return false; } return zoneId.equals(result.zoneId); } @Override public int hashCode() { int result = zoneId.hashCode(); result = 31 * result + (isOnlyMatch ? 1 : 0); return result; } @Override public String toString() { return "Result{" + "zoneId='" + zoneId + '\'' + ", isOnlyMatch=" + isOnlyMatch + '}'; } } /** * Finds a time zone ID that fits the supplied NITZ and country information. * * <p><em>Note:</em> When there are multiple matching zones then one of the matching candidates * will be returned. If the current device default zone matches it will be returned in * preference to other candidates. This method can return {@code null} if no matching time * zones are found. * The result of looking up a time zone using country information. */ public static final class CountryResult { /** A time zone for the country. */ public final String zoneId; /** * True if all the time zones in the country have the same offset at {@link #whenMillis}. */ public String guessZoneIdByNitzCountry(NitzData nitzData, String isoCountryCode) { return guessZoneIdByInstantOffsetDstCountry( nitzData.getCurrentTimeInMillis(), nitzData.getLocalOffsetMillis(), nitzData.isDst(), isoCountryCode); public final boolean allZonesHaveSameOffset; /** The time associated with {@link #allZonesHaveSameOffset}. */ public final long whenMillis; public CountryResult(String zoneId, boolean allZonesHaveSameOffset, long whenMillis) { this.zoneId = zoneId; this.allZonesHaveSameOffset = allZonesHaveSameOffset; this.whenMillis = whenMillis; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } CountryResult that = (CountryResult) o; if (allZonesHaveSameOffset != that.allZonesHaveSameOffset) { return false; } if (whenMillis != that.whenMillis) { return false; } return zoneId.equals(that.zoneId); } @Override public int hashCode() { int result = zoneId.hashCode(); result = 31 * result + (allZonesHaveSameOffset ? 1 : 0); result = 31 * result + (int) (whenMillis ^ (whenMillis >>> 32)); return result; } @Override public String toString() { return "CountryResult{" + "zoneId='" + zoneId + '\'' + ", allZonesHaveSameOffset=" + allZonesHaveSameOffset + ", whenMillis=" + whenMillis + '}'; } } private static final int MS_PER_HOUR = 60 * 60 * 1000; public TimeZoneLookupHelper() {} /** * Finds a time zone ID that fits the supplied time / offset and country information. * Looks for a time zone for the supplied NITZ and country information. * * <p><em>Note:</em> When there are multiple matching zones then one of the matching candidates * will be returned. If the current device default zone matches it will be returned in * preference to other candidates. This method can return {@code null} if no matching time * zones are found. * will be returned in the result. If the current device default zone matches it will be * returned in preference to other candidates. This method can return {@code null} if no * matching time zones are found. */ public String guessZoneIdByInstantOffsetDstCountry( long timeMillis, int utcOffsetMillis, boolean isDst, String isoCountryCode) { TimeZone timeZone = TimeUtils.getTimeZone(utcOffsetMillis, isDst, timeMillis, isoCountryCode); return timeZone == null ? null : timeZone.getID(); public OffsetResult lookupByNitzCountry(NitzData nitzData, String isoCountryCode) { CountryTimeZones countryTimeZones = TimeZoneFinder.getInstance().lookupCountryTimeZones(isoCountryCode); if (countryTimeZones == null) { return null; } android.icu.util.TimeZone bias = android.icu.util.TimeZone.getDefault(); CountryTimeZones.OffsetResult offsetResult = countryTimeZones.lookupByOffsetWithBias( nitzData.getLocalOffsetMillis(), nitzData.isDst(), nitzData.getCurrentTimeInMillis(), bias); if (offsetResult == null) { return null; } return new OffsetResult(offsetResult.mTimeZone.getID(), offsetResult.mOneMatch); } /** * Finds a time zone ID using only information present in the supplied {@link NitzData} object. * Looks for a time zone using only information present in the supplied {@link NitzData} object. * * <p><em>Note:</em> Because multiple time zones can have the same offset / DST state at a given * time this process is error prone; an arbitrary match is returned when there are multiple Loading @@ -74,9 +178,8 @@ public class TimeZoneLookupHelper { * information provided by NITZ is incorrect. This method can return {@code null} if no matching * time zones are found. */ public String guessZoneIdByNitz(NitzData nitzData) { TimeZone zone = guessZoneByNitzStatic(nitzData); return zone == null ? null : zone.getID(); public OffsetResult lookupByNitz(NitzData nitzData) { return lookupByNitzStatic(nitzData); } /** Loading @@ -86,52 +189,77 @@ public class TimeZoneLookupHelper { * according to the device's current system clock time). If this is not the case then * {@code null} can be returned. */ public String guessZoneIdByCountry(String isoCountryCode, long whenMillis) { public CountryResult lookupByCountry(String isoCountryCode, long whenMillis) { CountryTimeZones countryTimeZones = TimeZoneFinder.getInstance().lookupCountryTimeZones(isoCountryCode); if (countryTimeZones == null) { // Unknown country code. return null; } if (countryTimeZones.isDefaultOkForCountryTimeZoneDetection(whenMillis)) { return countryTimeZones.getDefaultTimeZoneId(); } if (countryTimeZones.getDefaultTimeZoneId() == null) { return null; } /** Static method for use by {@link ServiceStateTracker}. */ return new CountryResult( countryTimeZones.getDefaultTimeZoneId(), countryTimeZones.isDefaultOkForCountryTimeZoneDetection(whenMillis), whenMillis); } /** * Finds a time zone using only information present in the supplied {@link NitzData} object. * This is a static method for use by {@link ServiceStateTracker}. * * <p><em>Note:</em> Because multiple time zones can have the same offset / DST state at a given * time this process is error prone; an arbitrary match is returned when there are multiple * candidates. The algorithm can also return a non-exact match by assuming that the DST * information provided by NITZ is incorrect. This method can return {@code null} if no matching * time zones are found. */ static TimeZone guessZoneByNitzStatic(NitzData nitzData) { OffsetResult result = lookupByNitzStatic(nitzData); return result != null ? TimeZone.getTimeZone(result.zoneId) : null; } private static OffsetResult lookupByNitzStatic(NitzData nitzData) { int utcOffsetMillis = nitzData.getLocalOffsetMillis(); boolean isDst = nitzData.isDst(); long timeMillis = nitzData.getCurrentTimeInMillis(); TimeZone guess = guessByInstantOffsetDst(timeMillis, utcOffsetMillis, isDst); if (guess == null) { OffsetResult match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, isDst); if (match == null) { // Couldn't find a proper timezone. Perhaps the DST data is wrong. guess = guessByInstantOffsetDst(timeMillis, utcOffsetMillis, !isDst); match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, !isDst); } return guess; return match; } private static TimeZone guessByInstantOffsetDst(long timeMillis, int utcOffsetMillis, private static OffsetResult lookupByInstantOffsetDst(long timeMillis, int utcOffsetMillis, boolean isDst) { int rawOffset = utcOffsetMillis; if (isDst) { rawOffset -= MS_PER_HOUR; } String[] zones = TimeZone.getAvailableIDs(rawOffset); TimeZone guess = null; TimeZone match = null; Date d = new Date(timeMillis); boolean isOnlyMatch = true; for (String zone : zones) { TimeZone tz = TimeZone.getTimeZone(zone); if (tz.getOffset(timeMillis) == utcOffsetMillis && tz.inDaylightTime(d) == isDst) { guess = tz; if (match == null) { match = tz; } else { isOnlyMatch = false; break; } } } return guess; if (match == null) { return null; } return new OffsetResult(match.getID(), isOnlyMatch); } /** Loading @@ -145,9 +273,6 @@ public class TimeZoneLookupHelper { CountryTimeZones countryTimeZones = TimeZoneFinder.getInstance().lookupCountryTimeZones(isoCountryCode); if (countryTimeZones == null) { return false; } return countryTimeZones.hasUtcZone(whenMillis); return countryTimeZones != null && countryTimeZones.hasUtcZone(whenMillis); } } tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTest.java +27 −23 File changed.Preview size limit exceeded, changes collapsed. Show changes tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java +0 −4 Original line number Diff line number Diff line Loading @@ -187,8 +187,6 @@ public abstract class TelephonyTest { protected IntentBroadcaster mIntentBroadcaster; @Mock protected NitzStateMachine mNitzStateMachine; @Mock protected TimeServiceHelper mTimeServiceHelper; protected TelephonyManager mTelephonyManager; protected SubscriptionManager mSubscriptionManager; Loading Loading @@ -358,8 +356,6 @@ public abstract class TelephonyTest { .makeDeviceStateMonitor(nullable(Phone.class)); doReturn(mNitzStateMachine).when(mTelephonyComponentFactory) .makeNitzStateMachine(nullable(GsmCdmaPhone.class)); doReturn(mTimeServiceHelper).when(mTelephonyComponentFactory) .makeTimeServiceHelper(nullable(Context.class)); //mPhone doReturn(mContext).when(mPhone).getContext(); Loading Loading
src/java/com/android/internal/telephony/NitzStateMachine.java +55 −43 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import android.util.LocalLog; import android.util.TimeUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult; import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.util.TimeStampedValue; import com.android.internal.util.IndentingPrintWriter; Loading Loading @@ -165,7 +167,7 @@ public class NitzStateMachine { public NitzStateMachine(GsmCdmaPhone phone) { this(phone, TelephonyComponentFactory.getInstance().makeTimeServiceHelper(phone.getContext()), new TimeServiceHelper(phone.getContext()), new DeviceState(phone), new TimeZoneLookupHelper()); } Loading Loading @@ -218,10 +220,10 @@ public class NitzStateMachine { } if (countryChanged || mNeedCountryCodeForNitz) { // Capture the time zone property. This allows us to tell whether the device has a time // zone set. TimeZone.getDefault() returns a default zone (GMT) even when time zone is // not explicitly set making the system property a better indicator of whether an // explicit time zone choice has been made. // TimeZone.getDefault() returns a default zone (GMT) even when time zone have never // been set which makes it difficult to tell if it's what the user / time zone detection // has chosen. isTimeZoneSettingInitialized() tells us whether the time zone of the // device has ever been explicit set by the user or code. final boolean isTimeZoneSettingInitialized = mTimeServiceHelper.isTimeZoneSettingInitialized(); if (DBG) { Loading @@ -237,11 +239,13 @@ public class NitzStateMachine { // mNeedCountryCodeForNitz is only set to true when mLatestNitzSignal is set so // there's no need to check mLatestNitzSignal == null. zoneId = mTimeZoneLookupHelper.guessZoneIdByNitz(mLatestNitzSignal.mValue); OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitz(mLatestNitzSignal.mValue); if (DBG) { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: guessZoneIdByNitz() returned" + " zoneId=" + zoneId); + " lookupResult=" + lookupResult); } zoneId = lookupResult != null ? lookupResult.zoneId : null; } else if (mLatestNitzSignal == null) { zoneId = null; if (DBG) { Loading @@ -254,13 +258,21 @@ public class NitzStateMachine { && !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 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 // 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 (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); } zoneId = zone.getID(); if (mNeedCountryCodeForNitz) { Loading Loading @@ -302,25 +314,24 @@ public class NitzStateMachine { mWakeLock.release(); } } if (DBG) { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: using default TimeZone"); } } else { NitzData nitzData = mLatestNitzSignal.mValue; zoneId = mTimeZoneLookupHelper.guessZoneIdByNitzCountry( nitzData, isoCountryCode); OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, isoCountryCode); if (DBG) { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: using" + " guessZoneIdByNitzCountry(nitzData, isoCountryCode)," + " nitzData=" + nitzData + " isoCountryCode=" + isoCountryCode); + " isoCountryCode=" + isoCountryCode + " lookupResult=" + lookupResult); } zoneId = lookupResult != null ? lookupResult.zoneId : null; } } final String tmpLog = "handleNetworkCountryCodeSet:" + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized + " mLatestNitzSignal=" + mLatestNitzSignal + " iso-cc=" + isoCountryCode + " isoCountryCode=" + isoCountryCode + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz + " zoneId=" + zoneId; mTimeZoneLog.log(tmpLog); Loading @@ -334,10 +345,10 @@ public class NitzStateMachine { + " isTimeZoneDetectionEnabled() is false"); } if (mNeedCountryCodeForNitz) { saveNitzTimeZone(zoneId); mSavedTimeZoneId = zoneId; } } else { Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: zoneId == null, do nothing"); Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: lookupResult == null, do nothing"); } mNeedCountryCodeForNitz = false; } Loading Loading @@ -401,17 +412,20 @@ public class NitzStateMachine { if (!mGotCountryCode) { zoneId = null; } else if (!TextUtils.isEmpty(iso)) { zoneId = mTimeZoneLookupHelper.guessZoneIdByNitzCountry(newNitzData, iso); OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitzCountry(newNitzData, iso); zoneId = lookupResult != null ? lookupResult.zoneId : null; } else { // We don't have a valid iso country code. 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. zoneId = mTimeZoneLookupHelper.guessZoneIdByNitz(newNitzData); OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitz(newNitzData); if (DBG) { Rlog.d(LOG_TAG, "handleTimeZoneFromNitz: guessZoneIdByNitz returned" + " zoneId=" + zoneId); + " lookupResult=" + lookupResult); } zoneId = lookupResult != null ? lookupResult.zoneId : null; } } Loading @@ -425,7 +439,7 @@ public class NitzStateMachine { mLatestNitzSignal = nitzSignal; } String tmpLog = "handleTimeZoneFromNitz: nitzTimeSignal=" + nitzSignal String tmpLog = "handleTimeZoneFromNitz: nitzSignal=" + nitzSignal + " zoneId=" + zoneId + " iso=" + iso + " mGotCountryCode=" + mGotCountryCode + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz Loading @@ -441,7 +455,7 @@ public class NitzStateMachine { setAndBroadcastNetworkSetTimeZone(zoneId); } mNitzTimeZoneDetectionSuccessful = true; saveNitzTimeZone(zoneId); mSavedTimeZoneId = zoneId; } } catch (RuntimeException ex) { Rlog.e(LOG_TAG, "handleTimeZoneFromNitz: Processing NITZ data" Loading @@ -455,7 +469,7 @@ public class NitzStateMachine { || one.isDst() != two.isDst(); } private void handleTimeFromNitz(TimeStampedValue<NitzData> nitzTimeSignal) { private void handleTimeFromNitz(TimeStampedValue<NitzData> nitzSignal) { try { boolean ignoreNitz = mDeviceState.getIgnoreNitz(); if (ignoreNitz) { Loading @@ -471,24 +485,24 @@ public class NitzStateMachine { // Validate the nitzTimeSignal to reject obviously bogus elapsedRealtime values. long elapsedRealtime = mDeviceState.elapsedRealtime(); long millisSinceNitzReceived = elapsedRealtime - nitzTimeSignal.mElapsedRealtime; long millisSinceNitzReceived = elapsedRealtime - nitzSignal.mElapsedRealtime; if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) { if (DBG) { Rlog.d(LOG_TAG, "handleTimeFromNitz: not setting time, unexpected" + " elapsedRealtime=" + elapsedRealtime + " nitzTimeSignal=" + nitzTimeSignal); + " nitzSignal=" + nitzSignal); } return; } // Adjust the NITZ time by the delay since it was received to get the time now. long adjustedCurrentTimeMillis = nitzTimeSignal.mValue.getCurrentTimeInMillis() + millisSinceNitzReceived; nitzSignal.mValue.getCurrentTimeInMillis() + millisSinceNitzReceived; long gained = adjustedCurrentTimeMillis - mDeviceState.currentTimeMillis(); if (mTimeServiceHelper.isTimeDetectionEnabled()) { String logMsg = "handleTimeFromNitz:" + " nitzTimeSignal=" + nitzTimeSignal + " nitzSignal=" + nitzSignal + " adjustedCurrentTimeMillis=" + adjustedCurrentTimeMillis + " millisSinceNitzReceived= " + millisSinceNitzReceived + " gained=" + gained; Loading Loading @@ -526,24 +540,20 @@ public class NitzStateMachine { // Save the last NITZ time signal used so we can return to it later // if auto-time detection is toggled. mSavedNitzTime = new TimeStampedValue<>( adjustedCurrentTimeMillis, nitzTimeSignal.mElapsedRealtime); adjustedCurrentTimeMillis, nitzSignal.mElapsedRealtime); } finally { mWakeLock.release(); } } catch (RuntimeException ex) { Rlog.e(LOG_TAG, "handleTimeFromNitz: Processing NITZ data" + " nitzTimeSignal=" + nitzTimeSignal + " nitzSignal=" + nitzSignal + " ex=" + ex); } } private void saveNitzTimeZone(String zoneId) { mSavedTimeZoneId = zoneId; } private void setAndBroadcastNetworkSetTimeZone(String zoneId) { if (DBG) { Rlog.d(LOG_TAG, "setAndBroadcastNetworkSetTimeZone: setTimeZone=" + zoneId); Rlog.d(LOG_TAG, "setAndBroadcastNetworkSetTimeZone: zoneId=" + zoneId); } mTimeServiceHelper.setDeviceTimeZone(zoneId); if (DBG) { Loading Loading @@ -619,7 +629,7 @@ public class NitzStateMachine { pw.println(" mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz); pw.println(" mLatestNitzSignal=" + mLatestNitzSignal); pw.println(" mGotCountryCode=" + mGotCountryCode); pw.println(" mSavedTimeZone=" + mSavedTimeZoneId); pw.println(" mSavedTimeZoneId=" + mSavedTimeZoneId); pw.println(" mNitzTimeZoneDetectionSuccessful=" + mNitzTimeZoneDetectionSuccessful); // Miscellaneous Loading Loading @@ -649,20 +659,22 @@ public class NitzStateMachine { * @param iso Country code from network MCC */ private void updateTimeZoneByNetworkCountryCode(String iso) { String zoneId = mTimeZoneLookupHelper.guessZoneIdByCountry( CountryResult lookupResult = mTimeZoneLookupHelper.lookupByCountry( iso, mDeviceState.currentTimeMillis()); if (zoneId != null) { if (lookupResult != null && lookupResult.allZonesHaveSameOffset) { String logMsg = "updateTimeZoneByNetworkCountryCode: set time" + " zoneId=" + zoneId + " lookupResult=" + lookupResult + " iso=" + iso; if (DBG) { Rlog.d(LOG_TAG, logMsg); } mTimeZoneLog.log(logMsg); setAndBroadcastNetworkSetTimeZone(zoneId); setAndBroadcastNetworkSetTimeZone(lookupResult.zoneId); } else { if (DBG) { Rlog.d(LOG_TAG, "updateTimeZoneByNetworkCountryCode: no good zone for iso=" + iso); Rlog.d(LOG_TAG, "updateTimeZoneByNetworkCountryCode: no good zone for" + " iso=" + iso + " lookupResult=" + lookupResult); } } } Loading
src/java/com/android/internal/telephony/TelephonyComponentFactory.java +0 −7 Original line number Diff line number Diff line Loading @@ -68,13 +68,6 @@ public class TelephonyComponentFactory { return new NitzStateMachine(phone); } /** * Returns a new {@link TimeServiceHelper} instance. */ public TimeServiceHelper makeTimeServiceHelper(Context context) { return new TimeServiceHelper(context); } public SimActivationTracker makeSimActivationTracker(Phone phone) { return new SimActivationTracker(phone); } Loading
src/java/com/android/internal/telephony/TimeZoneLookupHelper.java +172 −47 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ package com.android.internal.telephony; import android.text.TextUtils; import android.util.TimeUtils; import libcore.util.CountryTimeZones; import libcore.util.TimeZoneFinder; Loading @@ -30,43 +29,148 @@ import java.util.TimeZone; */ // Non-final to allow mocking. public class TimeZoneLookupHelper { private static final int MS_PER_HOUR = 60 * 60 * 1000; public TimeZoneLookupHelper() {} /** * The result of looking up a time zone using offset information (and possibly more). */ public static final class OffsetResult { /** A zone that matches the supplied criteria. See also {@link #isOnlyMatch}. */ public final String zoneId; /** True if there is only one matching time zone for the supplied criteria. */ public final boolean isOnlyMatch; public OffsetResult(String zoneId, boolean isOnlyMatch) { this.zoneId = zoneId; this.isOnlyMatch = isOnlyMatch; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } OffsetResult result = (OffsetResult) o; if (isOnlyMatch != result.isOnlyMatch) { return false; } return zoneId.equals(result.zoneId); } @Override public int hashCode() { int result = zoneId.hashCode(); result = 31 * result + (isOnlyMatch ? 1 : 0); return result; } @Override public String toString() { return "Result{" + "zoneId='" + zoneId + '\'' + ", isOnlyMatch=" + isOnlyMatch + '}'; } } /** * Finds a time zone ID that fits the supplied NITZ and country information. * * <p><em>Note:</em> When there are multiple matching zones then one of the matching candidates * will be returned. If the current device default zone matches it will be returned in * preference to other candidates. This method can return {@code null} if no matching time * zones are found. * The result of looking up a time zone using country information. */ public static final class CountryResult { /** A time zone for the country. */ public final String zoneId; /** * True if all the time zones in the country have the same offset at {@link #whenMillis}. */ public String guessZoneIdByNitzCountry(NitzData nitzData, String isoCountryCode) { return guessZoneIdByInstantOffsetDstCountry( nitzData.getCurrentTimeInMillis(), nitzData.getLocalOffsetMillis(), nitzData.isDst(), isoCountryCode); public final boolean allZonesHaveSameOffset; /** The time associated with {@link #allZonesHaveSameOffset}. */ public final long whenMillis; public CountryResult(String zoneId, boolean allZonesHaveSameOffset, long whenMillis) { this.zoneId = zoneId; this.allZonesHaveSameOffset = allZonesHaveSameOffset; this.whenMillis = whenMillis; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } CountryResult that = (CountryResult) o; if (allZonesHaveSameOffset != that.allZonesHaveSameOffset) { return false; } if (whenMillis != that.whenMillis) { return false; } return zoneId.equals(that.zoneId); } @Override public int hashCode() { int result = zoneId.hashCode(); result = 31 * result + (allZonesHaveSameOffset ? 1 : 0); result = 31 * result + (int) (whenMillis ^ (whenMillis >>> 32)); return result; } @Override public String toString() { return "CountryResult{" + "zoneId='" + zoneId + '\'' + ", allZonesHaveSameOffset=" + allZonesHaveSameOffset + ", whenMillis=" + whenMillis + '}'; } } private static final int MS_PER_HOUR = 60 * 60 * 1000; public TimeZoneLookupHelper() {} /** * Finds a time zone ID that fits the supplied time / offset and country information. * Looks for a time zone for the supplied NITZ and country information. * * <p><em>Note:</em> When there are multiple matching zones then one of the matching candidates * will be returned. If the current device default zone matches it will be returned in * preference to other candidates. This method can return {@code null} if no matching time * zones are found. * will be returned in the result. If the current device default zone matches it will be * returned in preference to other candidates. This method can return {@code null} if no * matching time zones are found. */ public String guessZoneIdByInstantOffsetDstCountry( long timeMillis, int utcOffsetMillis, boolean isDst, String isoCountryCode) { TimeZone timeZone = TimeUtils.getTimeZone(utcOffsetMillis, isDst, timeMillis, isoCountryCode); return timeZone == null ? null : timeZone.getID(); public OffsetResult lookupByNitzCountry(NitzData nitzData, String isoCountryCode) { CountryTimeZones countryTimeZones = TimeZoneFinder.getInstance().lookupCountryTimeZones(isoCountryCode); if (countryTimeZones == null) { return null; } android.icu.util.TimeZone bias = android.icu.util.TimeZone.getDefault(); CountryTimeZones.OffsetResult offsetResult = countryTimeZones.lookupByOffsetWithBias( nitzData.getLocalOffsetMillis(), nitzData.isDst(), nitzData.getCurrentTimeInMillis(), bias); if (offsetResult == null) { return null; } return new OffsetResult(offsetResult.mTimeZone.getID(), offsetResult.mOneMatch); } /** * Finds a time zone ID using only information present in the supplied {@link NitzData} object. * Looks for a time zone using only information present in the supplied {@link NitzData} object. * * <p><em>Note:</em> Because multiple time zones can have the same offset / DST state at a given * time this process is error prone; an arbitrary match is returned when there are multiple Loading @@ -74,9 +178,8 @@ public class TimeZoneLookupHelper { * information provided by NITZ is incorrect. This method can return {@code null} if no matching * time zones are found. */ public String guessZoneIdByNitz(NitzData nitzData) { TimeZone zone = guessZoneByNitzStatic(nitzData); return zone == null ? null : zone.getID(); public OffsetResult lookupByNitz(NitzData nitzData) { return lookupByNitzStatic(nitzData); } /** Loading @@ -86,52 +189,77 @@ public class TimeZoneLookupHelper { * according to the device's current system clock time). If this is not the case then * {@code null} can be returned. */ public String guessZoneIdByCountry(String isoCountryCode, long whenMillis) { public CountryResult lookupByCountry(String isoCountryCode, long whenMillis) { CountryTimeZones countryTimeZones = TimeZoneFinder.getInstance().lookupCountryTimeZones(isoCountryCode); if (countryTimeZones == null) { // Unknown country code. return null; } if (countryTimeZones.isDefaultOkForCountryTimeZoneDetection(whenMillis)) { return countryTimeZones.getDefaultTimeZoneId(); } if (countryTimeZones.getDefaultTimeZoneId() == null) { return null; } /** Static method for use by {@link ServiceStateTracker}. */ return new CountryResult( countryTimeZones.getDefaultTimeZoneId(), countryTimeZones.isDefaultOkForCountryTimeZoneDetection(whenMillis), whenMillis); } /** * Finds a time zone using only information present in the supplied {@link NitzData} object. * This is a static method for use by {@link ServiceStateTracker}. * * <p><em>Note:</em> Because multiple time zones can have the same offset / DST state at a given * time this process is error prone; an arbitrary match is returned when there are multiple * candidates. The algorithm can also return a non-exact match by assuming that the DST * information provided by NITZ is incorrect. This method can return {@code null} if no matching * time zones are found. */ static TimeZone guessZoneByNitzStatic(NitzData nitzData) { OffsetResult result = lookupByNitzStatic(nitzData); return result != null ? TimeZone.getTimeZone(result.zoneId) : null; } private static OffsetResult lookupByNitzStatic(NitzData nitzData) { int utcOffsetMillis = nitzData.getLocalOffsetMillis(); boolean isDst = nitzData.isDst(); long timeMillis = nitzData.getCurrentTimeInMillis(); TimeZone guess = guessByInstantOffsetDst(timeMillis, utcOffsetMillis, isDst); if (guess == null) { OffsetResult match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, isDst); if (match == null) { // Couldn't find a proper timezone. Perhaps the DST data is wrong. guess = guessByInstantOffsetDst(timeMillis, utcOffsetMillis, !isDst); match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, !isDst); } return guess; return match; } private static TimeZone guessByInstantOffsetDst(long timeMillis, int utcOffsetMillis, private static OffsetResult lookupByInstantOffsetDst(long timeMillis, int utcOffsetMillis, boolean isDst) { int rawOffset = utcOffsetMillis; if (isDst) { rawOffset -= MS_PER_HOUR; } String[] zones = TimeZone.getAvailableIDs(rawOffset); TimeZone guess = null; TimeZone match = null; Date d = new Date(timeMillis); boolean isOnlyMatch = true; for (String zone : zones) { TimeZone tz = TimeZone.getTimeZone(zone); if (tz.getOffset(timeMillis) == utcOffsetMillis && tz.inDaylightTime(d) == isDst) { guess = tz; if (match == null) { match = tz; } else { isOnlyMatch = false; break; } } } return guess; if (match == null) { return null; } return new OffsetResult(match.getID(), isOnlyMatch); } /** Loading @@ -145,9 +273,6 @@ public class TimeZoneLookupHelper { CountryTimeZones countryTimeZones = TimeZoneFinder.getInstance().lookupCountryTimeZones(isoCountryCode); if (countryTimeZones == null) { return false; } return countryTimeZones.hasUtcZone(whenMillis); return countryTimeZones != null && countryTimeZones.hasUtcZone(whenMillis); } }
tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTest.java +27 −23 File changed.Preview size limit exceeded, changes collapsed. Show changes
tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java +0 −4 Original line number Diff line number Diff line Loading @@ -187,8 +187,6 @@ public abstract class TelephonyTest { protected IntentBroadcaster mIntentBroadcaster; @Mock protected NitzStateMachine mNitzStateMachine; @Mock protected TimeServiceHelper mTimeServiceHelper; protected TelephonyManager mTelephonyManager; protected SubscriptionManager mSubscriptionManager; Loading Loading @@ -358,8 +356,6 @@ public abstract class TelephonyTest { .makeDeviceStateMonitor(nullable(Phone.class)); doReturn(mNitzStateMachine).when(mTelephonyComponentFactory) .makeNitzStateMachine(nullable(GsmCdmaPhone.class)); doReturn(mTimeServiceHelper).when(mTelephonyComponentFactory) .makeTimeServiceHelper(nullable(Context.class)); //mPhone doReturn(mContext).when(mPhone).getContext(); Loading