Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.internal.telephony;
     18 
     19 import android.content.ContentResolver;
     20 import android.content.Context;
     21 import android.os.PowerManager;
     22 import android.os.SystemProperties;
     23 import android.provider.Settings;
     24 import android.telephony.Rlog;
     25 import android.telephony.TelephonyManager;
     26 import android.text.TextUtils;
     27 import android.util.LocalLog;
     28 import android.util.TimeUtils;
     29 
     30 import com.android.internal.annotations.VisibleForTesting;
     31 import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
     32 import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
     33 import com.android.internal.telephony.metrics.TelephonyMetrics;
     34 import com.android.internal.telephony.util.TimeStampedValue;
     35 import com.android.internal.util.IndentingPrintWriter;
     36 
     37 import java.io.FileDescriptor;
     38 import java.io.PrintWriter;
     39 import java.util.TimeZone;
     40 
     41 /**
     42  * {@hide}
     43  */
     44 // Non-final to allow mocking.
     45 public class NitzStateMachine {
     46 
     47     /**
     48      * A proxy over device state that allows things like system properties, system clock
     49      * to be faked for tests.
     50      */
     51     // Non-final to allow mocking.
     52     public static class DeviceState {
     53         private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10;
     54         private final int mNitzUpdateSpacing;
     55 
     56         private static final int NITZ_UPDATE_DIFF_DEFAULT = 2000;
     57         private final int mNitzUpdateDiff;
     58 
     59         private final GsmCdmaPhone mPhone;
     60         private final TelephonyManager mTelephonyManager;
     61         private final ContentResolver mCr;
     62 
     63         public DeviceState(GsmCdmaPhone phone) {
     64             mPhone = phone;
     65 
     66             Context context = phone.getContext();
     67             mTelephonyManager =
     68                     (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
     69             mCr = context.getContentResolver();
     70             mNitzUpdateSpacing =
     71                     SystemProperties.getInt("ro.nitz_update_spacing", NITZ_UPDATE_SPACING_DEFAULT);
     72             mNitzUpdateDiff =
     73                     SystemProperties.getInt("ro.nitz_update_diff", NITZ_UPDATE_DIFF_DEFAULT);
     74         }
     75 
     76         /**
     77          * If time between NITZ updates is less than {@link #getNitzUpdateSpacingMillis()} the
     78          * update may be ignored.
     79          */
     80         public int getNitzUpdateSpacingMillis() {
     81             return Settings.Global.getInt(mCr, Settings.Global.NITZ_UPDATE_SPACING,
     82                     mNitzUpdateSpacing);
     83         }
     84 
     85         /**
     86          * If {@link #getNitzUpdateSpacingMillis()} hasn't been exceeded but update is >
     87          * {@link #getNitzUpdateDiffMillis()} do the update
     88          */
     89         public int getNitzUpdateDiffMillis() {
     90             return Settings.Global.getInt(mCr, Settings.Global.NITZ_UPDATE_DIFF, mNitzUpdateDiff);
     91         }
     92 
     93         /**
     94          * Returns true if the {@code gsm.ignore-nitz} system property is set to "yes".
     95          */
     96         public boolean getIgnoreNitz() {
     97             String ignoreNitz = SystemProperties.get("gsm.ignore-nitz");
     98             return ignoreNitz != null && ignoreNitz.equals("yes");
     99         }
    100 
    101         public String getNetworkCountryIsoForPhone() {
    102             return mTelephonyManager.getNetworkCountryIsoForPhone(mPhone.getPhoneId());
    103         }
    104     }
    105 
    106     private static final String LOG_TAG = ServiceStateTracker.LOG_TAG;
    107     private static final boolean DBG = ServiceStateTracker.DBG;
    108 
    109     // Time detection state.
    110 
    111     /**
    112      * The last NITZ-sourced time considered. If auto time detection was off at the time this may
    113      * not have been used to set the device time, but it can be used if auto time detection is
    114      * re-enabled.
    115      */
    116     private TimeStampedValue<Long> mSavedNitzTime;
    117 
    118     // Time Zone detection state.
    119 
    120     /**
    121      * Sometimes we get the NITZ time before we know what country we are in. We keep the time zone
    122      * information from the NITZ string in mLatestNitzSignal so we can fix the time zone once we
    123      * know the country.
    124      */
    125     private boolean mNeedCountryCodeForNitz = false;
    126 
    127     private TimeStampedValue<NitzData> mLatestNitzSignal;
    128     private boolean mGotCountryCode = false;
    129     private String mSavedTimeZoneId;
    130 
    131     /**
    132      * Boolean is {@code true} if {@link #handleNitzReceived(TimeStampedValue)} has been called and
    133      * was able to determine a time zone (which may not ultimately have been used due to user
    134      * settings). Cleared by {@link #handleNetworkAvailable()} and
    135      * {@link #handleNetworkUnavailable()}. The flag can be used when historic NITZ data may no
    136      * longer be valid. {@code true} indicates it's not reasonable to try to set the time zone using
    137      * less reliable algorithms than NITZ-based detection such as by just using network country
    138      * code.
    139      */
    140     private boolean mNitzTimeZoneDetectionSuccessful = false;
    141 
    142     // Miscellaneous dependencies and helpers not related to detection state.
    143     private final LocalLog mTimeLog = new LocalLog(15);
    144     private final LocalLog mTimeZoneLog = new LocalLog(15);
    145     private final GsmCdmaPhone mPhone;
    146     private final DeviceState mDeviceState;
    147     private final TimeServiceHelper mTimeServiceHelper;
    148     private final TimeZoneLookupHelper mTimeZoneLookupHelper;
    149     /** Wake lock used while setting time of day. */
    150     private final PowerManager.WakeLock mWakeLock;
    151     private static final String WAKELOCK_TAG = "NitzStateMachine";
    152 
    153     public NitzStateMachine(GsmCdmaPhone phone) {
    154         this(phone,
    155                 new TimeServiceHelper(phone.getContext()),
    156                 new DeviceState(phone),
    157                 new TimeZoneLookupHelper());
    158     }
    159 
    160     @VisibleForTesting
    161     public NitzStateMachine(GsmCdmaPhone phone, TimeServiceHelper timeServiceHelper,
    162             DeviceState deviceState, TimeZoneLookupHelper timeZoneLookupHelper) {
    163         mPhone = phone;
    164 
    165         Context context = phone.getContext();
    166         PowerManager powerManager =
    167                 (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    168         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
    169 
    170         mDeviceState = deviceState;
    171         mTimeZoneLookupHelper = timeZoneLookupHelper;
    172         mTimeServiceHelper = timeServiceHelper;
    173         mTimeServiceHelper.setListener(new TimeServiceHelper.Listener() {
    174             @Override
    175             public void onTimeDetectionChange(boolean enabled) {
    176                 if (enabled) {
    177                     handleAutoTimeEnabled();
    178                 }
    179             }
    180 
    181             @Override
    182             public void onTimeZoneDetectionChange(boolean enabled) {
    183                 if (enabled) {
    184                     handleAutoTimeZoneEnabled();
    185                 }
    186             }
    187         });
    188     }
    189 
    190     /**
    191      * Called when the network country is set on the Phone. Although set, the network country code
    192      * may be invalid.
    193      *
    194      * @param countryChanged true when the country code is known to have changed, false if it
    195      *     probably hasn't
    196      */
    197     public void handleNetworkCountryCodeSet(boolean countryChanged) {
    198         mGotCountryCode = true;
    199 
    200         String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
    201         if (!TextUtils.isEmpty(isoCountryCode)
    202                 && !mNitzTimeZoneDetectionSuccessful
    203                 && mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
    204             updateTimeZoneByNetworkCountryCode(isoCountryCode);
    205         }
    206 
    207         if (countryChanged || mNeedCountryCodeForNitz) {
    208             // TimeZone.getDefault() returns a default zone (GMT) even when time zone have never
    209             // been set which makes it difficult to tell if it's what the user / time zone detection
    210             // has chosen. isTimeZoneSettingInitialized() tells us whether the time zone of the
    211             // device has ever been explicit set by the user or code.
    212             final boolean isTimeZoneSettingInitialized =
    213                     mTimeServiceHelper.isTimeZoneSettingInitialized();
    214             if (DBG) {
    215                 Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet:"
    216                         + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
    217                         + " mLatestNitzSignal=" + mLatestNitzSignal
    218                         + " isoCountryCode=" + isoCountryCode);
    219             }
    220             String zoneId;
    221             if (TextUtils.isEmpty(isoCountryCode) && mNeedCountryCodeForNitz) {
    222                 // Country code not found.  This is likely a test network.
    223                 // Get a TimeZone based only on the NITZ parameters (best guess).
    224 
    225                 // mNeedCountryCodeForNitz is only set to true when mLatestNitzSignal is set so
    226                 // there's no need to check mLatestNitzSignal == null.
    227                 OffsetResult lookupResult =
    228                         mTimeZoneLookupHelper.lookupByNitz(mLatestNitzSignal.mValue);
    229                 if (DBG) {
    230                     Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: guessZoneIdByNitz() returned"
    231                             + " lookupResult=" + lookupResult);
    232                 }
    233                 zoneId = lookupResult != null ? lookupResult.zoneId : null;
    234             } else if (mLatestNitzSignal == null) {
    235                 zoneId = null;
    236                 if (DBG) {
    237                     Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: No cached NITZ data available,"
    238                             + " not setting zone");
    239                 }
    240             } else { // mLatestNitzSignal != null
    241                 if (nitzOffsetMightBeBogus(mLatestNitzSignal.mValue)
    242                         && isTimeZoneSettingInitialized
    243                         && !countryUsesUtc(isoCountryCode, mLatestNitzSignal)) {
    244 
    245                     // This case means that (1) the device received an NITZ signal that could be
    246                     // bogus due to having a zero offset from UTC, (2) the device has had a time
    247                     // zone set explicitly and (3) the iso tells us the country is NOT one that uses
    248                     // a zero offset. This is interpreted as being NITZ incorrectly reporting a
    249                     // local time and not a UTC time. The zone is left as the current device's zone
    250                     // setting, and the system clock may be adjusted by taking the NITZ time and
    251                     // assuming the current zone setting is correct.
    252 
    253                     TimeZone zone = TimeZone.getDefault();
    254                     if (DBG) {
    255                         Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: NITZ looks bogus, maybe using"
    256                                 + " current default zone to adjust the system clock,"
    257                                 + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz
    258                                 + " mLatestNitzSignal=" + mLatestNitzSignal
    259                                 + " zone=" + zone);
    260                     }
    261                     zoneId = zone.getID();
    262 
    263                     if (mNeedCountryCodeForNitz) {
    264                         NitzData nitzData = mLatestNitzSignal.mValue;
    265                         try {
    266                             // Acquire the wakelock as we're reading the elapsed realtime clock
    267                             // here.
    268                             mWakeLock.acquire();
    269 
    270                             // Use the time that came with the NITZ offset that we think is bogus:
    271                             // we just interpret it as local time.
    272                             long ctm = nitzData.getCurrentTimeInMillis();
    273                             long delayAdjustedCtm = ctm + (mTimeServiceHelper.elapsedRealtime()
    274                                     - mLatestNitzSignal.mElapsedRealtime);
    275                             long tzOffset = zone.getOffset(delayAdjustedCtm);
    276                             if (DBG) {
    277                                 Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet:"
    278                                         + " tzOffset=" + tzOffset
    279                                         + " delayAdjustedCtm="
    280                                         + TimeUtils.logTimeOfDay(delayAdjustedCtm));
    281                             }
    282                             if (mTimeServiceHelper.isTimeDetectionEnabled()) {
    283                                 long timeZoneAdjustedCtm = delayAdjustedCtm - tzOffset;
    284                                 String msg = "handleNetworkCountryCodeSet: setting time"
    285                                         + " timeZoneAdjustedCtm="
    286                                         + TimeUtils.logTimeOfDay(timeZoneAdjustedCtm);
    287                                 setAndBroadcastNetworkSetTime(msg, timeZoneAdjustedCtm);
    288                             } else {
    289                                 // Adjust the saved NITZ time to account for tzOffset.
    290                                 mSavedNitzTime = new TimeStampedValue<>(
    291                                         mSavedNitzTime.mValue - tzOffset,
    292                                         mSavedNitzTime.mElapsedRealtime);
    293                                 if (DBG) {
    294                                     Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet:"
    295                                             + "adjusting time mSavedNitzTime=" + mSavedNitzTime);
    296                                 }
    297                             }
    298                         } finally {
    299                             mWakeLock.release();
    300                         }
    301                     }
    302                 } else {
    303                     NitzData nitzData = mLatestNitzSignal.mValue;
    304                     OffsetResult lookupResult =
    305                             mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, isoCountryCode);
    306                     if (DBG) {
    307                         Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: using"
    308                                 + " guessZoneIdByNitzCountry(nitzData, isoCountryCode),"
    309                                 + " nitzData=" + nitzData
    310                                 + " isoCountryCode=" + isoCountryCode
    311                                 + " lookupResult=" + lookupResult);
    312                     }
    313                     zoneId = lookupResult != null ? lookupResult.zoneId : null;
    314                 }
    315             }
    316             final String tmpLog = "handleNetworkCountryCodeSet:"
    317                     + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
    318                     + " mLatestNitzSignal=" + mLatestNitzSignal
    319                     + " isoCountryCode=" + isoCountryCode
    320                     + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz
    321                     + " zoneId=" + zoneId;
    322             mTimeZoneLog.log(tmpLog);
    323 
    324             if (zoneId != null) {
    325                 Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: zoneId != null, zoneId=" + zoneId);
    326                 if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
    327                     setAndBroadcastNetworkSetTimeZone(zoneId);
    328                 } else {
    329                     Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: skip changing zone as"
    330                             + " isTimeZoneDetectionEnabled() is false");
    331                 }
    332                 if (mNeedCountryCodeForNitz) {
    333                     mSavedTimeZoneId = zoneId;
    334                 }
    335             } else {
    336                 Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: lookupResult == null, do nothing");
    337             }
    338             mNeedCountryCodeForNitz = false;
    339         }
    340     }
    341 
    342     private boolean countryUsesUtc(
    343             String isoCountryCode, TimeStampedValue<NitzData> nitzSignal) {
    344         return mTimeZoneLookupHelper.countryUsesUtc(
    345                 isoCountryCode,
    346                 nitzSignal.mValue.getCurrentTimeInMillis());
    347     }
    348 
    349     /**
    350      * Informs the {@link NitzStateMachine} that the network has become available.
    351      */
    352     public void handleNetworkAvailable() {
    353         if (DBG) {
    354             Rlog.d(LOG_TAG, "handleNetworkAvailable: mNitzTimeZoneDetectionSuccessful="
    355                     + mNitzTimeZoneDetectionSuccessful
    356                     + ", Setting mNitzTimeZoneDetectionSuccessful=false");
    357         }
    358         mNitzTimeZoneDetectionSuccessful = false;
    359     }
    360 
    361     /**
    362      * Informs the {@link NitzStateMachine} that the network has become unavailable.
    363      */
    364     public void handleNetworkUnavailable() {
    365         if (DBG) {
    366             Rlog.d(LOG_TAG, "handleNetworkUnavailable");
    367         }
    368 
    369         mGotCountryCode = false;
    370         mNitzTimeZoneDetectionSuccessful = false;
    371     }
    372 
    373     /**
    374      * Returns {@code true} if the NITZ data looks like it might be incomplete or bogus, i.e. it has
    375      * a zero offset from UTC with either no DST information available or a zero DST offset.
    376      */
    377     private static boolean nitzOffsetMightBeBogus(NitzData nitzData) {
    378         return nitzData.getLocalOffsetMillis() == 0 && !nitzData.isDst();
    379     }
    380 
    381     /**
    382      * Handle a new NITZ signal being received.
    383      */
    384     public void handleNitzReceived(TimeStampedValue<NitzData> nitzSignal) {
    385         handleTimeZoneFromNitz(nitzSignal);
    386         handleTimeFromNitz(nitzSignal);
    387     }
    388 
    389     private void handleTimeZoneFromNitz(TimeStampedValue<NitzData> nitzSignal) {
    390         try {
    391             NitzData newNitzData = nitzSignal.mValue;
    392             String iso = mDeviceState.getNetworkCountryIsoForPhone();
    393             String zoneId;
    394             if (newNitzData.getEmulatorHostTimeZone() != null) {
    395                 zoneId = newNitzData.getEmulatorHostTimeZone().getID();
    396             } else {
    397                 if (!mGotCountryCode) {
    398                     zoneId = null;
    399                 } else if (!TextUtils.isEmpty(iso)) {
    400                     OffsetResult lookupResult =
    401                             mTimeZoneLookupHelper.lookupByNitzCountry(newNitzData, iso);
    402                     zoneId = lookupResult != null ? lookupResult.zoneId : null;
    403                 } else {
    404                     // We don't have a valid iso country code.  This is
    405                     // most likely because we're on a test network that's
    406                     // using a bogus MCC (eg, "001"), so get a TimeZone
    407                     // based only on the NITZ parameters.
    408                     OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitz(newNitzData);
    409                     if (DBG) {
    410                         Rlog.d(LOG_TAG, "handleTimeZoneFromNitz: guessZoneIdByNitz returned"
    411                                 + " lookupResult=" + lookupResult);
    412                     }
    413                     zoneId = lookupResult != null ? lookupResult.zoneId : null;
    414                 }
    415             }
    416 
    417             if ((zoneId == null)
    418                     || mLatestNitzSignal == null
    419                     || offsetInfoDiffers(newNitzData, mLatestNitzSignal.mValue)) {
    420                 // We got the time before the country, or the zone has changed
    421                 // so we don't know how to identify the DST rules yet.  Save
    422                 // the information and hope to fix it up later.
    423                 mNeedCountryCodeForNitz = true;
    424                 mLatestNitzSignal = nitzSignal;
    425             }
    426 
    427             String tmpLog = "handleTimeZoneFromNitz: nitzSignal=" + nitzSignal
    428                     + " zoneId=" + zoneId
    429                     + " iso=" + iso + " mGotCountryCode=" + mGotCountryCode
    430                     + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz
    431                     + " isTimeZoneDetectionEnabled()="
    432                     + mTimeServiceHelper.isTimeZoneDetectionEnabled();
    433             if (DBG) {
    434                 Rlog.d(LOG_TAG, tmpLog);
    435             }
    436             mTimeZoneLog.log(tmpLog);
    437 
    438             if (zoneId != null) {
    439                 if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
    440                     setAndBroadcastNetworkSetTimeZone(zoneId);
    441                 }
    442                 mNitzTimeZoneDetectionSuccessful = true;
    443                 mSavedTimeZoneId = zoneId;
    444             }
    445         } catch (RuntimeException ex) {
    446             Rlog.e(LOG_TAG, "handleTimeZoneFromNitz: Processing NITZ data"
    447                     + " nitzSignal=" + nitzSignal
    448                     + " ex=" + ex);
    449         }
    450     }
    451 
    452     private static boolean offsetInfoDiffers(NitzData one, NitzData two) {
    453         return one.getLocalOffsetMillis() != two.getLocalOffsetMillis()
    454                 || one.isDst() != two.isDst();
    455     }
    456 
    457     private void handleTimeFromNitz(TimeStampedValue<NitzData> nitzSignal) {
    458         try {
    459             boolean ignoreNitz = mDeviceState.getIgnoreNitz();
    460             if (ignoreNitz) {
    461                 Rlog.d(LOG_TAG,
    462                         "handleTimeFromNitz: Not setting clock because gsm.ignore-nitz is set");
    463                 return;
    464             }
    465 
    466             try {
    467                 // Acquire the wake lock as we are reading the elapsed realtime clock and system
    468                 // clock.
    469                 mWakeLock.acquire();
    470 
    471                 // Validate the nitzTimeSignal to reject obviously bogus elapsedRealtime values.
    472                 long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
    473                 long millisSinceNitzReceived = elapsedRealtime - nitzSignal.mElapsedRealtime;
    474                 if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) {
    475                     if (DBG) {
    476                         Rlog.d(LOG_TAG, "handleTimeFromNitz: not setting time, unexpected"
    477                                 + " elapsedRealtime=" + elapsedRealtime
    478                                 + " nitzSignal=" + nitzSignal);
    479                     }
    480                     return;
    481                 }
    482 
    483                 // Adjust the NITZ time by the delay since it was received to get the time now.
    484                 long adjustedCurrentTimeMillis =
    485                         nitzSignal.mValue.getCurrentTimeInMillis() + millisSinceNitzReceived;
    486                 long gained = adjustedCurrentTimeMillis - mTimeServiceHelper.currentTimeMillis();
    487 
    488                 if (mTimeServiceHelper.isTimeDetectionEnabled()) {
    489                     String logMsg = "handleTimeFromNitz:"
    490                             + " nitzSignal=" + nitzSignal
    491                             + " adjustedCurrentTimeMillis=" + adjustedCurrentTimeMillis
    492                             + " millisSinceNitzReceived= " + millisSinceNitzReceived
    493                             + " gained=" + gained;
    494 
    495                     if (mSavedNitzTime == null) {
    496                         logMsg += ": First update received.";
    497                         setAndBroadcastNetworkSetTime(logMsg, adjustedCurrentTimeMillis);
    498                     } else {
    499                         long elapsedRealtimeSinceLastSaved = mTimeServiceHelper.elapsedRealtime()
    500                                 - mSavedNitzTime.mElapsedRealtime;
    501                         int nitzUpdateSpacing = mDeviceState.getNitzUpdateSpacingMillis();
    502                         int nitzUpdateDiff = mDeviceState.getNitzUpdateDiffMillis();
    503                         if (elapsedRealtimeSinceLastSaved > nitzUpdateSpacing
    504                                 || Math.abs(gained) > nitzUpdateDiff) {
    505                             // Either it has been a while since we received an update, or the gain
    506                             // is sufficiently large that we want to act on it.
    507                             logMsg += ": New update received.";
    508                             setAndBroadcastNetworkSetTime(logMsg, adjustedCurrentTimeMillis);
    509                         } else {
    510                             if (DBG) {
    511                                 Rlog.d(LOG_TAG, logMsg + ": Update throttled.");
    512                             }
    513 
    514                             // Return early. This means that we don't reset the
    515                             // mSavedNitzTime for next time and that we may act on more
    516                             // NITZ time signals overall but should end up with a system clock that
    517                             // tracks NITZ more closely than if we saved throttled values (which
    518                             // would reset mSavedNitzTime.elapsedRealtime used to calculate time
    519                             // since the last NITZ signal was received).
    520                             return;
    521                         }
    522                     }
    523                 }
    524 
    525                 // Save the last NITZ time signal used so we can return to it later
    526                 // if auto-time detection is toggled.
    527                 mSavedNitzTime = new TimeStampedValue<>(
    528                         adjustedCurrentTimeMillis, nitzSignal.mElapsedRealtime);
    529             } finally {
    530                 mWakeLock.release();
    531             }
    532         } catch (RuntimeException ex) {
    533             Rlog.e(LOG_TAG, "handleTimeFromNitz: Processing NITZ data"
    534                     + " nitzSignal=" + nitzSignal
    535                     + " ex=" + ex);
    536         }
    537     }
    538 
    539     private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
    540         if (DBG) {
    541             Rlog.d(LOG_TAG, "setAndBroadcastNetworkSetTimeZone: zoneId=" + zoneId);
    542         }
    543         mTimeServiceHelper.setDeviceTimeZone(zoneId);
    544         if (DBG) {
    545             Rlog.d(LOG_TAG,
    546                     "setAndBroadcastNetworkSetTimeZone: called setDeviceTimeZone()"
    547                             + " zoneId=" + zoneId);
    548         }
    549     }
    550 
    551     private void setAndBroadcastNetworkSetTime(String msg, long time) {
    552         if (!mWakeLock.isHeld()) {
    553             Rlog.w(LOG_TAG, "setAndBroadcastNetworkSetTime: Wake lock not held while setting device"
    554                     + " time (msg=" + msg + ")");
    555         }
    556 
    557         msg = "setAndBroadcastNetworkSetTime: [Setting time to time=" + time + "]:" + msg;
    558         if (DBG) {
    559             Rlog.d(LOG_TAG, msg);
    560         }
    561         mTimeLog.log(msg);
    562         mTimeServiceHelper.setDeviceTime(time);
    563         TelephonyMetrics.getInstance().writeNITZEvent(mPhone.getPhoneId(), time);
    564     }
    565 
    566     private void handleAutoTimeEnabled() {
    567         if (DBG) {
    568             Rlog.d(LOG_TAG, "handleAutoTimeEnabled: Reverting to NITZ Time:"
    569                     + " mSavedNitzTime=" + mSavedNitzTime);
    570         }
    571         if (mSavedNitzTime != null) {
    572             try {
    573                 // Acquire the wakelock as we're reading the elapsed realtime clock here.
    574                 mWakeLock.acquire();
    575 
    576                 long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
    577                 String msg = "mSavedNitzTime: Reverting to NITZ time"
    578                         + " elapsedRealtime=" + elapsedRealtime
    579                         + " mSavedNitzTime=" + mSavedNitzTime;
    580                 long adjustedCurrentTimeMillis =
    581                         mSavedNitzTime.mValue + (elapsedRealtime - mSavedNitzTime.mElapsedRealtime);
    582                 setAndBroadcastNetworkSetTime(msg, adjustedCurrentTimeMillis);
    583             } finally {
    584                 mWakeLock.release();
    585             }
    586         }
    587     }
    588 
    589     private void handleAutoTimeZoneEnabled() {
    590         String tmpLog = "handleAutoTimeZoneEnabled: Reverting to NITZ TimeZone:"
    591                 + " mSavedTimeZoneId=" + mSavedTimeZoneId;
    592         if (DBG) {
    593             Rlog.d(LOG_TAG, tmpLog);
    594         }
    595         mTimeZoneLog.log(tmpLog);
    596         if (mSavedTimeZoneId != null) {
    597             setAndBroadcastNetworkSetTimeZone(mSavedTimeZoneId);
    598         } else {
    599             String iso = mDeviceState.getNetworkCountryIsoForPhone();
    600             if (!TextUtils.isEmpty(iso)) {
    601                 updateTimeZoneByNetworkCountryCode(iso);
    602             }
    603         }
    604     }
    605 
    606     /**
    607      * Dumps the current in-memory state to the supplied PrintWriter.
    608      */
    609     public void dumpState(PrintWriter pw) {
    610         // Time Detection State
    611         pw.println(" mSavedTime=" + mSavedNitzTime);
    612 
    613         // Time Zone Detection State
    614         pw.println(" mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz);
    615         pw.println(" mLatestNitzSignal=" + mLatestNitzSignal);
    616         pw.println(" mGotCountryCode=" + mGotCountryCode);
    617         pw.println(" mSavedTimeZoneId=" + mSavedTimeZoneId);
    618         pw.println(" mNitzTimeZoneDetectionSuccessful=" + mNitzTimeZoneDetectionSuccessful);
    619 
    620         // Miscellaneous
    621         pw.println(" mWakeLock=" + mWakeLock);
    622         pw.flush();
    623     }
    624 
    625     /**
    626      * Dumps the time / time zone logs to the supplied IndentingPrintWriter.
    627      */
    628     public void dumpLogs(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
    629         ipw.println(" Time Logs:");
    630         ipw.increaseIndent();
    631         mTimeLog.dump(fd, ipw, args);
    632         ipw.decreaseIndent();
    633 
    634         ipw.println(" Time zone Logs:");
    635         ipw.increaseIndent();
    636         mTimeZoneLog.dump(fd, ipw, args);
    637         ipw.decreaseIndent();
    638     }
    639 
    640     /**
    641      * Update time zone by network country code, works well on countries which only have one time
    642      * zone or multiple zones with the same offset.
    643      *
    644      * @param iso Country code from network MCC
    645      */
    646     private void updateTimeZoneByNetworkCountryCode(String iso) {
    647         CountryResult lookupResult = mTimeZoneLookupHelper.lookupByCountry(
    648                 iso, mTimeServiceHelper.currentTimeMillis());
    649         if (lookupResult != null && lookupResult.allZonesHaveSameOffset) {
    650             String logMsg = "updateTimeZoneByNetworkCountryCode: set time"
    651                     + " lookupResult=" + lookupResult
    652                     + " iso=" + iso;
    653             if (DBG) {
    654                 Rlog.d(LOG_TAG, logMsg);
    655             }
    656             mTimeZoneLog.log(logMsg);
    657             setAndBroadcastNetworkSetTimeZone(lookupResult.zoneId);
    658         } else {
    659             if (DBG) {
    660                 Rlog.d(LOG_TAG, "updateTimeZoneByNetworkCountryCode: no good zone for"
    661                         + " iso=" + iso
    662                         + " lookupResult=" + lookupResult);
    663             }
    664         }
    665     }
    666 
    667     /**
    668      * Get the mNitzTimeZoneDetectionSuccessful flag value.
    669      */
    670     public boolean getNitzTimeZoneDetectionSuccessful() {
    671         return mNitzTimeZoneDetectionSuccessful;
    672     }
    673 
    674     /**
    675      * Returns the last NITZ data that was cached.
    676      */
    677     public NitzData getCachedNitzData() {
    678         return mLatestNitzSignal != null ? mLatestNitzSignal.mValue : null;
    679     }
    680 
    681     /**
    682      * Returns the time zone ID from the most recent time that a time zone could be determined by
    683      * this state machine.
    684      */
    685     public String getSavedTimeZoneId() {
    686         return mSavedTimeZoneId;
    687     }
    688 
    689 }
    690