Loading src/java/com/android/internal/telephony/NitzData.java +4 −44 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.telephony.Rlog; import com.android.internal.annotations.VisibleForTesting; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; /** Loading @@ -35,7 +34,6 @@ import java.util.TimeZone; @VisibleForTesting(visibility = PACKAGE) public final class NitzData { private static final String LOG_TAG = ServiceStateTracker.LOG_TAG; private static final int MS_PER_HOUR = 60 * 60 * 1000; private static final int MS_PER_QUARTER_HOUR = 15 * 60 * 1000; /* Time stamp after 19 January 2038 is not supported under 32 bit */ Loading @@ -53,7 +51,7 @@ public final class NitzData { private final TimeZone mEmulatorHostTimeZone; private NitzData(String originalString, int zoneOffsetMillis, Integer dstOffsetMillis, long utcTimeMillis, TimeZone timeZone) { long utcTimeMillis, TimeZone emulatorHostTimeZone) { if (originalString == null) { throw new NullPointerException("originalString==null"); } Loading @@ -61,7 +59,7 @@ public final class NitzData { this.mZoneOffset = zoneOffsetMillis; this.mDstOffset = dstOffsetMillis; this.mCurrentTimeMillis = utcTimeMillis; this.mEmulatorHostTimeZone = timeZone; this.mEmulatorHostTimeZone = emulatorHostTimeZone; } /** Loading Loading @@ -139,9 +137,9 @@ public final class NitzData { /** A method for use in tests to create NitzData instances. */ public static NitzData createForTests(int zoneOffsetMillis, Integer dstOffsetMillis, long utcTimeMillis, TimeZone timeZone) { long utcTimeMillis, TimeZone emulatorHostTimeZone) { return new NitzData("Test data", zoneOffsetMillis, dstOffsetMillis, utcTimeMillis, timeZone); emulatorHostTimeZone); } /** Loading Loading @@ -187,44 +185,6 @@ public final class NitzData { return mEmulatorHostTimeZone; } /** * Using information present in the supplied {@link NitzData} object, guess the time zone. * 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 time zones are found. */ public static TimeZone guessTimeZone(NitzData nitzData) { int offset = nitzData.getLocalOffsetMillis(); boolean dst = nitzData.isDst(); long when = nitzData.getCurrentTimeInMillis(); TimeZone guess = findTimeZone(offset, dst, when); if (guess == null) { // Couldn't find a proper timezone. Perhaps the DST data is wrong. guess = findTimeZone(offset, !dst, when); } return guess; } private static TimeZone findTimeZone(int offset, boolean dst, long when) { int rawOffset = offset; if (dst) { rawOffset -= MS_PER_HOUR; } String[] zones = TimeZone.getAvailableIDs(rawOffset); TimeZone guess = null; Date d = new Date(when); for (String zone : zones) { TimeZone tz = TimeZone.getTimeZone(zone); if (tz.getOffset(when) == offset && tz.inDaylightTime(d) == dst) { guess = tz; break; } } return guess; } @Override public boolean equals(Object o) { if (this == o) { Loading src/java/com/android/internal/telephony/NitzStateMachine.java +191 −237 File changed.Preview size limit exceeded, changes collapsed. Show changes src/java/com/android/internal/telephony/ServiceStateTracker.java +57 −75 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ import com.android.internal.telephony.uicc.SIMRecords; import com.android.internal.telephony.uicc.UiccCardApplication; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.util.NotificationChannelController; import com.android.internal.telephony.util.TimeStampedValue; import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; Loading Loading @@ -559,7 +560,7 @@ public class ServiceStateTracker extends Handler { mMin = null; mPrlVersion = null; mIsMinInfoReady = false; mNitzState.clearNitzTimeZoneDetectionSuccessful(); mNitzState.handleNetworkUnavailable(); //cancel any pending pollstate request on voice tech switching cancelPollState(); Loading Loading @@ -2547,8 +2548,7 @@ public class ServiceStateTracker extends Handler { mNewSS.setStateOutOfService(); mNewCellLoc.setStateInvalid(); setSignalStrengthDefaultValues(); mNitzState.setNetworkCountryIsoAvailable(false); mNitzState.clearNitzTimeZoneDetectionSuccessful(); mNitzState.handleNetworkUnavailable(); pollStateDone(); break; Loading @@ -2556,8 +2556,7 @@ public class ServiceStateTracker extends Handler { mNewSS.setStateOff(); mNewCellLoc.setStateInvalid(); setSignalStrengthDefaultValues(); mNitzState.setNetworkCountryIsoAvailable(false); mNitzState.clearNitzTimeZoneDetectionSuccessful(); mNitzState.handleNetworkUnavailable(); // don't poll when device is shutting down or the poll was not modemTrigged // (they sent us new radio data) and current network is not IWLAN if (mDeviceShuttingDown || Loading Loading @@ -2788,18 +2787,12 @@ public class ServiceStateTracker extends Handler { if (hasRegistered) { mNetworkAttachedRegistrants.notifyRegistrants(); if (DBG) { log("pollStateDone: hasRegistered," + " current mNitzState.getNitzTimeZoneDetectionSuccessful()=" + mNitzState.getNitzTimeZoneDetectionSuccessful() + ". Calling mNitzState.clearNitzTimeZoneDetectionSuccessful()"); } mNitzState.clearNitzTimeZoneDetectionSuccessful(); mNitzState.handleNetworkAvailable(); } if (hasDeregistered) { mNetworkDetachedRegistrants.notifyRegistrants(); mNitzState.handleNetworkUnavailable(); } if (hasRejectCauseChanged) { Loading @@ -2812,6 +2805,7 @@ public class ServiceStateTracker extends Handler { tm.setNetworkOperatorNameForPhone(mPhone.getPhoneId(), mSS.getOperatorAlpha()); String prevOperatorNumeric = tm.getNetworkOperatorForPhone(mPhone.getPhoneId()); String prevCountryIsoCode = tm.getNetworkCountryIso(mPhone.getPhoneId()); String operatorNumeric = mSS.getOperatorNumeric(); if (!mPhone.isPhoneTypeGsm()) { Loading @@ -2828,48 +2822,47 @@ public class ServiceStateTracker extends Handler { if (isInvalidOperatorNumeric(operatorNumeric)) { if (DBG) log("operatorNumeric " + operatorNumeric + " is invalid"); tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), ""); mNitzState.setNetworkCountryIsoAvailable(false); mNitzState.clearNitzTimeZoneDetectionSuccessful(); mNitzState.handleNetworkUnavailable(); } else if (mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) { // Update time zone, ISO, and IDD. // // If the device is on IWLAN, modems manufacture a ServiceState with the MCC/MNC of // the SIM as if we were talking to towers. Telephony code then uses that with // mccTable to suggest a timezone. We shouldn't do that if the MCC/MNC is from IWLAN String iso = ""; String mcc = ""; // Update IDD. if (!mPhone.isPhoneTypeGsm()) { setOperatorIdd(operatorNumeric); } // Update ISO. String countryIsoCode = ""; try { mcc = operatorNumeric.substring(0, 3); iso = MccTable.countryCodeForMcc(Integer.parseInt(mcc)); String mcc = operatorNumeric.substring(0, 3); countryIsoCode = MccTable.countryCodeForMcc(Integer.parseInt(mcc)); } catch (NumberFormatException | StringIndexOutOfBoundsException ex) { loge("pollStateDone: countryCodeForMcc error: " + ex); } tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), countryIsoCode); tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), iso); mNitzState.setNetworkCountryIsoAvailable(true); if (!mcc.equals("000") && !TextUtils.isEmpty(iso) && mNitzState.shouldUpdateTimeZoneUsingCountryCode()) { mNitzState.updateTimeZoneByNetworkCountryCode(iso); } if (!mPhone.isPhoneTypeGsm()) { setOperatorIdd(operatorNumeric); } // Update Time Zone. boolean iccCardExists = iccCardExists(); boolean networkIsoChanged = networkCountryIsoChanged(countryIsoCode, prevCountryIsoCode); boolean mccChanged = mccChanged(operatorNumeric, prevOperatorNumeric); boolean fixTimeZoneCallNeeded = mNitzState.fixTimeZoneCallNeeded(); if (mccChanged || fixTimeZoneCallNeeded) { // fixTimeZoneCallNeeded == need to fix it because when the NITZ time // came in we didn't know the country code. // Determine countryChanged: networkIso is only reliable if there's an ICC card. boolean countryChanged = iccCardExists && networkIsoChanged; if (DBG) { log("shouldFixTimeZoneNow: mccChanged=" + mccChanged + " fixTimeZoneCallNeeded=" + fixTimeZoneCallNeeded); } mNitzState.fixTimeZone(iso); long ctm = System.currentTimeMillis(); log("Before handleNetworkCountryCodeKnown:" + " countryChanged=" + countryChanged + " iccCardExist=" + iccCardExists + " countryIsoChanged=" + networkIsoChanged + " operatorNumeric=" + operatorNumeric + " prevOperatorNumeric=" + prevOperatorNumeric + " countryIsoCode=" + countryIsoCode + " prevCountryIsoCode=" + prevCountryIsoCode + " ltod=" + TimeUtils.logTimeOfDay(ctm)); } mNitzState.handleNetworkCountryCodeSet(countryChanged); } tm.setNetworkRoamingForPhone(mPhone.getPhoneId(), Loading Loading @@ -3092,7 +3085,7 @@ public class ServiceStateTracker extends Handler { if (lastNitzData == null) { tzone = null; } else { tzone = NitzData.guessTimeZone(lastNitzData); tzone = TimeZoneLookupHelper.guessZoneByNitzStatic(lastNitzData); if (ServiceStateTracker.DBG) { log("fixUnknownMcc(): guessNitzTimeZone returned " + (tzone == null ? tzone : tzone.getID())); Loading Loading @@ -3433,7 +3426,9 @@ public class ServiceStateTracker extends Handler { NitzData newNitzData = NitzData.parse(nitzString); if (newNitzData != null) { try { mNitzState.setTimeAndTimeZoneFromNitz(newNitzData, nitzReceiveTime); TimeStampedValue<NitzData> nitzSignal = new TimeStampedValue<>(newNitzData, nitzReceiveTime); mNitzState.handleNitzReceived(nitzSignal); } finally { if (DBG) { long end = SystemClock.elapsedRealtime(); Loading Loading @@ -4034,49 +4029,36 @@ public class ServiceStateTracker extends Handler { } /** * Return true if the operator changed. * Return true if the network operator's country code changed. */ private boolean mccChanged(String operatorNumeric, String prevOperatorNumeric) { // Return false if the mcc isn't valid as we don't know where we are. // Return true if we have an IccCard and the mcc changed. private boolean networkCountryIsoChanged(String newCountryIsoCode, String prevCountryIsoCode) { // Return false if the new ISO code isn't valid as we don't know where we are. // Return true if the previous ISO code wasn't valid, or if it was and the new one differs. // If mcc is invalid then we'll return false int mcc; try { mcc = Integer.parseInt(operatorNumeric.substring(0, 3)); } catch (Exception e) { // If newCountryIsoCode is invalid then we'll return false if (TextUtils.isEmpty(newCountryIsoCode)) { if (DBG) { log("mccChanged: no mcc, operatorNumeric=" + operatorNumeric + " retVal=false"); log("countryIsoChanged: no new country ISO code"); } return false; } // If prevMcc is invalid will make it different from mcc // so we'll return true if the card exists. int prevMcc; try { prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3)); } catch (Exception e) { prevMcc = mcc + 1; if (TextUtils.isEmpty(prevCountryIsoCode)) { if (DBG) { log("countryIsoChanged: no previous country ISO code"); } return true; } return !newCountryIsoCode.equals(prevCountryIsoCode); } // Determine if the Icc card exists private boolean iccCardExists() { boolean iccCardExist = false; if (mUiccApplcation != null) { iccCardExist = mUiccApplcation.getState() != AppState.APPSTATE_UNKNOWN; } // Determine retVal boolean retVal = iccCardExist && (mcc != prevMcc); if (DBG) { long ctm = System.currentTimeMillis(); log("shouldFixTimeZoneNow: retVal=" + retVal + " iccCardExist=" + iccCardExist + " operatorNumeric=" + operatorNumeric + " mcc=" + mcc + " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc + " ltod=" + TimeUtils.logTimeOfDay(ctm)); } return retVal; return iccCardExist; } public String getSystemProperty(String property, String defValue) { Loading src/java/com/android/internal/telephony/TimeZoneLookupHelper.java 0 → 100644 +173 −0 Original line number Diff line number Diff line /* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony; import android.util.TimeUtils; import libcore.util.CountryTimeZones; import libcore.util.TimeZoneFinder; import java.util.Arrays; import java.util.Date; import java.util.TimeZone; /** * An interface to various time zone lookup behaviors. */ // Non-final to allow mocking. public class TimeZoneLookupHelper { private static final int MS_PER_HOUR = 60 * 60 * 1000; /** * List of ISO codes for countries that can have an offset of * GMT+0 when not in daylight savings time. This ignores some * small places such as the Canary Islands (Spain) and * Danmarkshavn (Denmark). The list must be sorted by code. */ private static final String[] GMT_COUNTRY_CODES = { "bf", // Burkina Faso "ci", // Cote d'Ivoire "eh", // Western Sahara "fo", // Faroe Islands, Denmark "gb", // United Kingdom of Great Britain and Northern Ireland "gh", // Ghana "gm", // Gambia "gn", // Guinea "gw", // Guinea Bissau "ie", // Ireland "lr", // Liberia "is", // Iceland "ma", // Morocco "ml", // Mali "mr", // Mauritania "pt", // Portugal "sl", // Sierra Leone "sn", // Senegal "st", // Sao Tome and Principe "tg", // Togo }; public TimeZoneLookupHelper() {} /** * 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. */ public String guessZoneIdByNitzCountry(NitzData nitzData, String isoCountryCode) { return guessZoneIdByInstantOffsetDstCountry( nitzData.getCurrentTimeInMillis(), nitzData.getLocalOffsetMillis(), nitzData.isDst(), isoCountryCode); } /** * Finds a time zone ID that fits the supplied time / offset 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. */ 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(); } /** * Finds a time zone ID 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 * 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. */ public String guessZoneIdByNitz(NitzData nitzData) { TimeZone zone = guessZoneByNitzStatic(nitzData); return zone == null ? null : zone.getID(); } /** * Returns a time zone ID for the country if possible. For counties that use a single time zone * this will provide a good choice. For countries with multiple time zones, a time zone is * returned if all time zones used in the country currently have the same offset (currently == * 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) { CountryTimeZones countryTimeZones = TimeZoneFinder.getInstance().lookupCountryTimeZones(isoCountryCode); if (countryTimeZones == null) { // Unknown country code. return null; } if (countryTimeZones.isDefaultOkForCountryTimeZoneDetection(whenMillis)) { return countryTimeZones.getDefaultTimeZoneId(); } return null; } /** Static method for use by {@link ServiceStateTracker}. */ static TimeZone guessZoneByNitzStatic(NitzData nitzData) { int utcOffsetMillis = nitzData.getLocalOffsetMillis(); boolean isDst = nitzData.isDst(); long timeMillis = nitzData.getCurrentTimeInMillis(); TimeZone guess = guessByInstantOffsetDst(timeMillis, utcOffsetMillis, isDst); if (guess == null) { // Couldn't find a proper timezone. Perhaps the DST data is wrong. guess = guessByInstantOffsetDst(timeMillis, utcOffsetMillis, !isDst); } return guess; } private static TimeZone guessByInstantOffsetDst(long timeMillis, int utcOffsetMillis, boolean isDst) { int rawOffset = utcOffsetMillis; if (isDst) { rawOffset -= MS_PER_HOUR; } String[] zones = TimeZone.getAvailableIDs(rawOffset); TimeZone guess = null; Date d = new Date(timeMillis); for (String zone : zones) { TimeZone tz = TimeZone.getTimeZone(zone); if (tz.getOffset(timeMillis) == utcOffsetMillis && tz.inDaylightTime(d) == isDst) { guess = tz; break; } } return guess; } /** * Returns {@code true} if the supplied (lower-case) ISO country code is for a country known to * use a raw offset of zero from UTC. */ public boolean countryUsesUtc(String isoCountryCode) { return Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode) >= 0; } } src/java/com/android/internal/telephony/util/TimeStampedValue.java 0 → 100644 +73 −0 Original line number Diff line number Diff line /* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony.util; import android.os.SystemClock; /** * A pair containing a value and an associated time stamp. * * @param <T> The type of the value. */ public final class TimeStampedValue<T> { /** The value. */ public final T mValue; /** * The value of {@link SystemClock#elapsedRealtime} or equivalent when value was * determined. */ public final long mElapsedRealtime; public TimeStampedValue(T value, long elapsedRealtime) { this.mValue = value; this.mElapsedRealtime = elapsedRealtime; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TimeStampedValue<?> that = (TimeStampedValue<?>) o; if (mElapsedRealtime != that.mElapsedRealtime) { return false; } return mValue != null ? mValue.equals(that.mValue) : that.mValue == null; } @Override public int hashCode() { int result = mValue != null ? mValue.hashCode() : 0; result = 31 * result + (int) (mElapsedRealtime ^ (mElapsedRealtime >>> 32)); return result; } @Override public String toString() { return "TimeStampedValue{" + "mValue=" + mValue + ", elapsedRealtime=" + mElapsedRealtime + '}'; } } Loading
src/java/com/android/internal/telephony/NitzData.java +4 −44 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.telephony.Rlog; import com.android.internal.annotations.VisibleForTesting; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; /** Loading @@ -35,7 +34,6 @@ import java.util.TimeZone; @VisibleForTesting(visibility = PACKAGE) public final class NitzData { private static final String LOG_TAG = ServiceStateTracker.LOG_TAG; private static final int MS_PER_HOUR = 60 * 60 * 1000; private static final int MS_PER_QUARTER_HOUR = 15 * 60 * 1000; /* Time stamp after 19 January 2038 is not supported under 32 bit */ Loading @@ -53,7 +51,7 @@ public final class NitzData { private final TimeZone mEmulatorHostTimeZone; private NitzData(String originalString, int zoneOffsetMillis, Integer dstOffsetMillis, long utcTimeMillis, TimeZone timeZone) { long utcTimeMillis, TimeZone emulatorHostTimeZone) { if (originalString == null) { throw new NullPointerException("originalString==null"); } Loading @@ -61,7 +59,7 @@ public final class NitzData { this.mZoneOffset = zoneOffsetMillis; this.mDstOffset = dstOffsetMillis; this.mCurrentTimeMillis = utcTimeMillis; this.mEmulatorHostTimeZone = timeZone; this.mEmulatorHostTimeZone = emulatorHostTimeZone; } /** Loading Loading @@ -139,9 +137,9 @@ public final class NitzData { /** A method for use in tests to create NitzData instances. */ public static NitzData createForTests(int zoneOffsetMillis, Integer dstOffsetMillis, long utcTimeMillis, TimeZone timeZone) { long utcTimeMillis, TimeZone emulatorHostTimeZone) { return new NitzData("Test data", zoneOffsetMillis, dstOffsetMillis, utcTimeMillis, timeZone); emulatorHostTimeZone); } /** Loading Loading @@ -187,44 +185,6 @@ public final class NitzData { return mEmulatorHostTimeZone; } /** * Using information present in the supplied {@link NitzData} object, guess the time zone. * 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 time zones are found. */ public static TimeZone guessTimeZone(NitzData nitzData) { int offset = nitzData.getLocalOffsetMillis(); boolean dst = nitzData.isDst(); long when = nitzData.getCurrentTimeInMillis(); TimeZone guess = findTimeZone(offset, dst, when); if (guess == null) { // Couldn't find a proper timezone. Perhaps the DST data is wrong. guess = findTimeZone(offset, !dst, when); } return guess; } private static TimeZone findTimeZone(int offset, boolean dst, long when) { int rawOffset = offset; if (dst) { rawOffset -= MS_PER_HOUR; } String[] zones = TimeZone.getAvailableIDs(rawOffset); TimeZone guess = null; Date d = new Date(when); for (String zone : zones) { TimeZone tz = TimeZone.getTimeZone(zone); if (tz.getOffset(when) == offset && tz.inDaylightTime(d) == dst) { guess = tz; break; } } return guess; } @Override public boolean equals(Object o) { if (this == o) { Loading
src/java/com/android/internal/telephony/NitzStateMachine.java +191 −237 File changed.Preview size limit exceeded, changes collapsed. Show changes
src/java/com/android/internal/telephony/ServiceStateTracker.java +57 −75 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ import com.android.internal.telephony.uicc.SIMRecords; import com.android.internal.telephony.uicc.UiccCardApplication; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.util.NotificationChannelController; import com.android.internal.telephony.util.TimeStampedValue; import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; Loading Loading @@ -559,7 +560,7 @@ public class ServiceStateTracker extends Handler { mMin = null; mPrlVersion = null; mIsMinInfoReady = false; mNitzState.clearNitzTimeZoneDetectionSuccessful(); mNitzState.handleNetworkUnavailable(); //cancel any pending pollstate request on voice tech switching cancelPollState(); Loading Loading @@ -2547,8 +2548,7 @@ public class ServiceStateTracker extends Handler { mNewSS.setStateOutOfService(); mNewCellLoc.setStateInvalid(); setSignalStrengthDefaultValues(); mNitzState.setNetworkCountryIsoAvailable(false); mNitzState.clearNitzTimeZoneDetectionSuccessful(); mNitzState.handleNetworkUnavailable(); pollStateDone(); break; Loading @@ -2556,8 +2556,7 @@ public class ServiceStateTracker extends Handler { mNewSS.setStateOff(); mNewCellLoc.setStateInvalid(); setSignalStrengthDefaultValues(); mNitzState.setNetworkCountryIsoAvailable(false); mNitzState.clearNitzTimeZoneDetectionSuccessful(); mNitzState.handleNetworkUnavailable(); // don't poll when device is shutting down or the poll was not modemTrigged // (they sent us new radio data) and current network is not IWLAN if (mDeviceShuttingDown || Loading Loading @@ -2788,18 +2787,12 @@ public class ServiceStateTracker extends Handler { if (hasRegistered) { mNetworkAttachedRegistrants.notifyRegistrants(); if (DBG) { log("pollStateDone: hasRegistered," + " current mNitzState.getNitzTimeZoneDetectionSuccessful()=" + mNitzState.getNitzTimeZoneDetectionSuccessful() + ". Calling mNitzState.clearNitzTimeZoneDetectionSuccessful()"); } mNitzState.clearNitzTimeZoneDetectionSuccessful(); mNitzState.handleNetworkAvailable(); } if (hasDeregistered) { mNetworkDetachedRegistrants.notifyRegistrants(); mNitzState.handleNetworkUnavailable(); } if (hasRejectCauseChanged) { Loading @@ -2812,6 +2805,7 @@ public class ServiceStateTracker extends Handler { tm.setNetworkOperatorNameForPhone(mPhone.getPhoneId(), mSS.getOperatorAlpha()); String prevOperatorNumeric = tm.getNetworkOperatorForPhone(mPhone.getPhoneId()); String prevCountryIsoCode = tm.getNetworkCountryIso(mPhone.getPhoneId()); String operatorNumeric = mSS.getOperatorNumeric(); if (!mPhone.isPhoneTypeGsm()) { Loading @@ -2828,48 +2822,47 @@ public class ServiceStateTracker extends Handler { if (isInvalidOperatorNumeric(operatorNumeric)) { if (DBG) log("operatorNumeric " + operatorNumeric + " is invalid"); tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), ""); mNitzState.setNetworkCountryIsoAvailable(false); mNitzState.clearNitzTimeZoneDetectionSuccessful(); mNitzState.handleNetworkUnavailable(); } else if (mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) { // Update time zone, ISO, and IDD. // // If the device is on IWLAN, modems manufacture a ServiceState with the MCC/MNC of // the SIM as if we were talking to towers. Telephony code then uses that with // mccTable to suggest a timezone. We shouldn't do that if the MCC/MNC is from IWLAN String iso = ""; String mcc = ""; // Update IDD. if (!mPhone.isPhoneTypeGsm()) { setOperatorIdd(operatorNumeric); } // Update ISO. String countryIsoCode = ""; try { mcc = operatorNumeric.substring(0, 3); iso = MccTable.countryCodeForMcc(Integer.parseInt(mcc)); String mcc = operatorNumeric.substring(0, 3); countryIsoCode = MccTable.countryCodeForMcc(Integer.parseInt(mcc)); } catch (NumberFormatException | StringIndexOutOfBoundsException ex) { loge("pollStateDone: countryCodeForMcc error: " + ex); } tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), countryIsoCode); tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), iso); mNitzState.setNetworkCountryIsoAvailable(true); if (!mcc.equals("000") && !TextUtils.isEmpty(iso) && mNitzState.shouldUpdateTimeZoneUsingCountryCode()) { mNitzState.updateTimeZoneByNetworkCountryCode(iso); } if (!mPhone.isPhoneTypeGsm()) { setOperatorIdd(operatorNumeric); } // Update Time Zone. boolean iccCardExists = iccCardExists(); boolean networkIsoChanged = networkCountryIsoChanged(countryIsoCode, prevCountryIsoCode); boolean mccChanged = mccChanged(operatorNumeric, prevOperatorNumeric); boolean fixTimeZoneCallNeeded = mNitzState.fixTimeZoneCallNeeded(); if (mccChanged || fixTimeZoneCallNeeded) { // fixTimeZoneCallNeeded == need to fix it because when the NITZ time // came in we didn't know the country code. // Determine countryChanged: networkIso is only reliable if there's an ICC card. boolean countryChanged = iccCardExists && networkIsoChanged; if (DBG) { log("shouldFixTimeZoneNow: mccChanged=" + mccChanged + " fixTimeZoneCallNeeded=" + fixTimeZoneCallNeeded); } mNitzState.fixTimeZone(iso); long ctm = System.currentTimeMillis(); log("Before handleNetworkCountryCodeKnown:" + " countryChanged=" + countryChanged + " iccCardExist=" + iccCardExists + " countryIsoChanged=" + networkIsoChanged + " operatorNumeric=" + operatorNumeric + " prevOperatorNumeric=" + prevOperatorNumeric + " countryIsoCode=" + countryIsoCode + " prevCountryIsoCode=" + prevCountryIsoCode + " ltod=" + TimeUtils.logTimeOfDay(ctm)); } mNitzState.handleNetworkCountryCodeSet(countryChanged); } tm.setNetworkRoamingForPhone(mPhone.getPhoneId(), Loading Loading @@ -3092,7 +3085,7 @@ public class ServiceStateTracker extends Handler { if (lastNitzData == null) { tzone = null; } else { tzone = NitzData.guessTimeZone(lastNitzData); tzone = TimeZoneLookupHelper.guessZoneByNitzStatic(lastNitzData); if (ServiceStateTracker.DBG) { log("fixUnknownMcc(): guessNitzTimeZone returned " + (tzone == null ? tzone : tzone.getID())); Loading Loading @@ -3433,7 +3426,9 @@ public class ServiceStateTracker extends Handler { NitzData newNitzData = NitzData.parse(nitzString); if (newNitzData != null) { try { mNitzState.setTimeAndTimeZoneFromNitz(newNitzData, nitzReceiveTime); TimeStampedValue<NitzData> nitzSignal = new TimeStampedValue<>(newNitzData, nitzReceiveTime); mNitzState.handleNitzReceived(nitzSignal); } finally { if (DBG) { long end = SystemClock.elapsedRealtime(); Loading Loading @@ -4034,49 +4029,36 @@ public class ServiceStateTracker extends Handler { } /** * Return true if the operator changed. * Return true if the network operator's country code changed. */ private boolean mccChanged(String operatorNumeric, String prevOperatorNumeric) { // Return false if the mcc isn't valid as we don't know where we are. // Return true if we have an IccCard and the mcc changed. private boolean networkCountryIsoChanged(String newCountryIsoCode, String prevCountryIsoCode) { // Return false if the new ISO code isn't valid as we don't know where we are. // Return true if the previous ISO code wasn't valid, or if it was and the new one differs. // If mcc is invalid then we'll return false int mcc; try { mcc = Integer.parseInt(operatorNumeric.substring(0, 3)); } catch (Exception e) { // If newCountryIsoCode is invalid then we'll return false if (TextUtils.isEmpty(newCountryIsoCode)) { if (DBG) { log("mccChanged: no mcc, operatorNumeric=" + operatorNumeric + " retVal=false"); log("countryIsoChanged: no new country ISO code"); } return false; } // If prevMcc is invalid will make it different from mcc // so we'll return true if the card exists. int prevMcc; try { prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3)); } catch (Exception e) { prevMcc = mcc + 1; if (TextUtils.isEmpty(prevCountryIsoCode)) { if (DBG) { log("countryIsoChanged: no previous country ISO code"); } return true; } return !newCountryIsoCode.equals(prevCountryIsoCode); } // Determine if the Icc card exists private boolean iccCardExists() { boolean iccCardExist = false; if (mUiccApplcation != null) { iccCardExist = mUiccApplcation.getState() != AppState.APPSTATE_UNKNOWN; } // Determine retVal boolean retVal = iccCardExist && (mcc != prevMcc); if (DBG) { long ctm = System.currentTimeMillis(); log("shouldFixTimeZoneNow: retVal=" + retVal + " iccCardExist=" + iccCardExist + " operatorNumeric=" + operatorNumeric + " mcc=" + mcc + " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc + " ltod=" + TimeUtils.logTimeOfDay(ctm)); } return retVal; return iccCardExist; } public String getSystemProperty(String property, String defValue) { Loading
src/java/com/android/internal/telephony/TimeZoneLookupHelper.java 0 → 100644 +173 −0 Original line number Diff line number Diff line /* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony; import android.util.TimeUtils; import libcore.util.CountryTimeZones; import libcore.util.TimeZoneFinder; import java.util.Arrays; import java.util.Date; import java.util.TimeZone; /** * An interface to various time zone lookup behaviors. */ // Non-final to allow mocking. public class TimeZoneLookupHelper { private static final int MS_PER_HOUR = 60 * 60 * 1000; /** * List of ISO codes for countries that can have an offset of * GMT+0 when not in daylight savings time. This ignores some * small places such as the Canary Islands (Spain) and * Danmarkshavn (Denmark). The list must be sorted by code. */ private static final String[] GMT_COUNTRY_CODES = { "bf", // Burkina Faso "ci", // Cote d'Ivoire "eh", // Western Sahara "fo", // Faroe Islands, Denmark "gb", // United Kingdom of Great Britain and Northern Ireland "gh", // Ghana "gm", // Gambia "gn", // Guinea "gw", // Guinea Bissau "ie", // Ireland "lr", // Liberia "is", // Iceland "ma", // Morocco "ml", // Mali "mr", // Mauritania "pt", // Portugal "sl", // Sierra Leone "sn", // Senegal "st", // Sao Tome and Principe "tg", // Togo }; public TimeZoneLookupHelper() {} /** * 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. */ public String guessZoneIdByNitzCountry(NitzData nitzData, String isoCountryCode) { return guessZoneIdByInstantOffsetDstCountry( nitzData.getCurrentTimeInMillis(), nitzData.getLocalOffsetMillis(), nitzData.isDst(), isoCountryCode); } /** * Finds a time zone ID that fits the supplied time / offset 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. */ 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(); } /** * Finds a time zone ID 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 * 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. */ public String guessZoneIdByNitz(NitzData nitzData) { TimeZone zone = guessZoneByNitzStatic(nitzData); return zone == null ? null : zone.getID(); } /** * Returns a time zone ID for the country if possible. For counties that use a single time zone * this will provide a good choice. For countries with multiple time zones, a time zone is * returned if all time zones used in the country currently have the same offset (currently == * 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) { CountryTimeZones countryTimeZones = TimeZoneFinder.getInstance().lookupCountryTimeZones(isoCountryCode); if (countryTimeZones == null) { // Unknown country code. return null; } if (countryTimeZones.isDefaultOkForCountryTimeZoneDetection(whenMillis)) { return countryTimeZones.getDefaultTimeZoneId(); } return null; } /** Static method for use by {@link ServiceStateTracker}. */ static TimeZone guessZoneByNitzStatic(NitzData nitzData) { int utcOffsetMillis = nitzData.getLocalOffsetMillis(); boolean isDst = nitzData.isDst(); long timeMillis = nitzData.getCurrentTimeInMillis(); TimeZone guess = guessByInstantOffsetDst(timeMillis, utcOffsetMillis, isDst); if (guess == null) { // Couldn't find a proper timezone. Perhaps the DST data is wrong. guess = guessByInstantOffsetDst(timeMillis, utcOffsetMillis, !isDst); } return guess; } private static TimeZone guessByInstantOffsetDst(long timeMillis, int utcOffsetMillis, boolean isDst) { int rawOffset = utcOffsetMillis; if (isDst) { rawOffset -= MS_PER_HOUR; } String[] zones = TimeZone.getAvailableIDs(rawOffset); TimeZone guess = null; Date d = new Date(timeMillis); for (String zone : zones) { TimeZone tz = TimeZone.getTimeZone(zone); if (tz.getOffset(timeMillis) == utcOffsetMillis && tz.inDaylightTime(d) == isDst) { guess = tz; break; } } return guess; } /** * Returns {@code true} if the supplied (lower-case) ISO country code is for a country known to * use a raw offset of zero from UTC. */ public boolean countryUsesUtc(String isoCountryCode) { return Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode) >= 0; } }
src/java/com/android/internal/telephony/util/TimeStampedValue.java 0 → 100644 +73 −0 Original line number Diff line number Diff line /* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony.util; import android.os.SystemClock; /** * A pair containing a value and an associated time stamp. * * @param <T> The type of the value. */ public final class TimeStampedValue<T> { /** The value. */ public final T mValue; /** * The value of {@link SystemClock#elapsedRealtime} or equivalent when value was * determined. */ public final long mElapsedRealtime; public TimeStampedValue(T value, long elapsedRealtime) { this.mValue = value; this.mElapsedRealtime = elapsedRealtime; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TimeStampedValue<?> that = (TimeStampedValue<?>) o; if (mElapsedRealtime != that.mElapsedRealtime) { return false; } return mValue != null ? mValue.equals(that.mValue) : that.mValue == null; } @Override public int hashCode() { int result = mValue != null ? mValue.hashCode() : 0; result = 31 * result + (int) (mElapsedRealtime ^ (mElapsedRealtime >>> 32)); return result; } @Override public String toString() { return "TimeStampedValue{" + "mValue=" + mValue + ", elapsedRealtime=" + mElapsedRealtime + '}'; } }