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 DeviceState(GsmCdmaPhone phone)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 */ getNitzUpdateSpacingMillis()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 */ getNitzUpdateDiffMillis()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 */ getIgnoreNitz()96 public boolean getIgnoreNitz() { 97 String ignoreNitz = SystemProperties.get("gsm.ignore-nitz"); 98 return ignoreNitz != null && ignoreNitz.equals("yes"); 99 } 100 getNetworkCountryIsoForPhone()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 NitzStateMachine(GsmCdmaPhone phone)153 public NitzStateMachine(GsmCdmaPhone phone) { 154 this(phone, 155 new TimeServiceHelper(phone.getContext()), 156 new DeviceState(phone), 157 new TimeZoneLookupHelper()); 158 } 159 160 @VisibleForTesting NitzStateMachine(GsmCdmaPhone phone, TimeServiceHelper timeServiceHelper, DeviceState deviceState, TimeZoneLookupHelper timeZoneLookupHelper)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 */ handleNetworkCountryCodeSet(boolean countryChanged)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 countryUsesUtc( String isoCountryCode, TimeStampedValue<NitzData> nitzSignal)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 */ handleNetworkAvailable()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 */ handleNetworkUnavailable()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 */ nitzOffsetMightBeBogus(NitzData nitzData)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 */ handleNitzReceived(TimeStampedValue<NitzData> nitzSignal)384 public void handleNitzReceived(TimeStampedValue<NitzData> nitzSignal) { 385 handleTimeZoneFromNitz(nitzSignal); 386 handleTimeFromNitz(nitzSignal); 387 } 388 handleTimeZoneFromNitz(TimeStampedValue<NitzData> nitzSignal)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 offsetInfoDiffers(NitzData one, NitzData two)452 private static boolean offsetInfoDiffers(NitzData one, NitzData two) { 453 return one.getLocalOffsetMillis() != two.getLocalOffsetMillis() 454 || one.isDst() != two.isDst(); 455 } 456 handleTimeFromNitz(TimeStampedValue<NitzData> nitzSignal)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 setAndBroadcastNetworkSetTimeZone(String zoneId)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 setAndBroadcastNetworkSetTime(String msg, long time)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 handleAutoTimeEnabled()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 handleAutoTimeZoneEnabled()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 */ dumpState(PrintWriter pw)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 */ dumpLogs(FileDescriptor fd, IndentingPrintWriter ipw, String[] args)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 */ updateTimeZoneByNetworkCountryCode(String iso)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 */ getNitzTimeZoneDetectionSuccessful()670 public boolean getNitzTimeZoneDetectionSuccessful() { 671 return mNitzTimeZoneDetectionSuccessful; 672 } 673 674 /** 675 * Returns the last NITZ data that was cached. 676 */ getCachedNitzData()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 */ getSavedTimeZoneId()685 public String getSavedTimeZoneId() { 686 return mSavedTimeZoneId; 687 } 688 689 } 690