Android9.0 automatic update mechanism of local time zone and local time
brief introduction
Now there are two ways for Android to synchronize time through the network: NITZ and NTP. They use different conditions and can obtain different information; After checking the automatic synchronization function, the phone will first try NITZ mode. If the time acquisition fails, NTP mode will be used
1.NITZ(network identity and time zone) synchronization time
NITZ is a GSM/WCDMA base station mode, and SIM card must be inserted; Time and time zone information can be provided
2.NTP(network time protocol) synchronization time
NTP is used when there is no SIM card or the operator does not support NITZ. It only obtains the time through the network (GPRS/WIFI), and only provides time information without time zone information (therefore, in areas that do not support NITZ, the function of automatically obtaining time zone is actually invalid)
NTP also has a caching mechanism: the current time successfully obtained will be saved. When the user turns on the automatic time update function next time, the time will be updated in combination with the mobile phone clock. This is also the reason why the mobile phone can automatically update the time when there is no network.
NITZ time update process:
The SIM card reports information, and the operator provides time zone information and time information to the terminal for updating
flow chart:
Requires com android. The phone process starts when Android intent. action. BOOT_ When the completed broadcast is sent out, com android. The phone process will start and call onCreate, com. Of Application android. The Application class of the phone process is phoneapp Java is as follows:
/packages/services/Telephony/src/com/android/phone/PhoneApp.java 35 @Override 36 public void onCreate() { 37 if (UserHandle.myUserId() == 0) { 38 // We are running as the primary user, so should bring up the 39 // global phone state. 40 mPhoneGlobals = new PhoneGlobals(this); 41 mPhoneGlobals.onCreate(); 42 43 mTelephonyGlobals = new TelephonyGlobals(this); 44 mTelephonyGlobals.onCreate(); 45 } 46 }
You can see that onCreate initializes two global classes, PhoneGlobals and TelephonyGlobals, and calls their onCreate function. The automatic update of time is completed in onCreate of PhoneGlobals
/packages/services/Telephony/src/com/android/phone/PhoneGlobals.java 269 public void onCreate() { 270 if (VDBG) Log.v(LOG_TAG, "onCreate()..."); ... 284 if (mCM == null) { 285 // Initialize the telephony framework 286 PhoneFactory.makeDefaultPhones(this); ... }
/frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneFactory.java 102 /** 103 * FIXME replace this with some other way of making these 104 * instances 105 */ 106 public static void makeDefaultPhone(Context context) { 107 synchronized (sLockProxyPhones) { 108 142 143 /* In case of multi SIM mode two instances of Phone, RIL are created, 144 where as in single SIM mode only instance. isMultiSimEnabled() function checks 145 whether it is single SIM or multi SIM mode */ //Get the number of sim cards 146 int numPhones = TelephonyManager.getDefault().getPhoneCount(); 159 .... 160 int[] networkModes = new int[numPhones]; 161 sPhones = new Phone[numPhones]; 162 sCommandsInterfaces = new RIL[numPhones]; 163 sTelephonyNetworkFactories = new TelephonyNetworkFactory[numPhones]; 164 165 for (int i = 0; i < numPhones; i++) { 166 // reads the system properties and makes commandsinterface 167 // Get preferred network type. 168 networkModes[i] = RILConstants.PREFERRED_NETWORK_MODE; 169 170 Rlog.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkModes[i])); 171 sCommandsInterfaces[i] = new RIL(context, networkModes[i], 172 cdmaSubscription, i); 173 } ... 187 for (int i = 0; i < numPhones; i++) { 188 Phone phone = null; 189 int phoneType = TelephonyManager.getPhoneType(networkModes[i]); 190 if (phoneType == PhoneConstants.PHONE_TYPE_GSM) { 191 phone = new GsmCdmaPhone(context, 192 sCommandsInterfaces[i], sPhoneNotifier, i, 193 PhoneConstants.PHONE_TYPE_GSM, 194 TelephonyComponentFactory.getInstance()); 195 } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) { 196 phone = new GsmCdmaPhone(context, 197 sCommandsInterfaces[i], sPhoneNotifier, i, 198 PhoneConstants.PHONE_TYPE_CDMA_LTE, 199 TelephonyComponentFactory.getInstance()); 200 } 201 Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i); 202 203 sPhones[i] = phone; 204 } 205 206 .... 263 } 264 } 265 }
The above function will create RIL instances according to the number of sim cards. The main function of RIL instances is to establish contact with haldevice and set some callbacks to halservice Android hardware. radio@1.2 -Radio service, which is implemented as ril_service.cpp. Then, gmcdmaphone is created with RIL instance as parameter, which is mainly used to bind the functions that process the time and time zone information transmitted from hal layer. Here are the details of these two steps:
1. Create RIL instance and set callback to hal service
/frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java 445 public RIL(Context context, int preferredNetworkType, 446 int cdmaSubscription, Integer instanceId) { 447 super(context); 462 463 mRadioResponse = new RadioResponse(this); 464 mRadioIndication = new RadioIndication(this); 485 486 // set radio callback; needed to set RadioIndication callback (should be done after 487 // wakelock stuff is initialized above as callbacks are received on separate binder threads) 488 getRadioProxy(null); 489 ... 490 } 351 /** Returns a {@link IRadio} instance or null if the service is not available. */ 352 @VisibleForTesting 353 public IRadio getRadioProxy(Message result) { 354 if (!mIsMobileNetworkSupported) { 355 //Mobile network is not supported 362 } 363 364 if (mRadioProxy != null) { 365 return mRadioProxy; 366 } 367 368 try { 369 mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId], 370 true); 371 if (mRadioProxy != null) { 372 mRadioProxy.linkToDeath(mRadioProxyDeathRecipient, 373 mRadioProxyCookie.incrementAndGet()); //Set the RadioResponse instance and RadioIndication instance when RIL is instantiated to hal service 374 mRadioProxy.setResponseFunctions(mRadioResponse, mRadioIndication); 375 } else { 376 riljLoge("getRadioProxy: mRadioProxy == null"); 377 } 378 } catch (RemoteException | RuntimeException e) { 379 mRadioProxy = null; 380 riljLoge("RadioProxy getService/setResponseFunctions: " + e); 381 } ... 393 return mRadioProxy; 394 }
As mentioned before, the implementation of hal Service obtained by RIL is ril_service.cpp will call RIL_ setResponseFunctions of service:
/hardware/ril/libril/ril_service.cpp 790Return<void> RadioImpl::setResponseFunctions( 791 const ::android::sp<IRadioResponse>& radioResponseParam, 792 const ::android::sp<IRadioIndication>& radioIndicationParam) { 793 RLOGD("setResponseFunctions"); 794 795 pthread_rwlock_t *radioServiceRwlockPtr = radio::getRadioServiceRwlock(mSlotId); 796 int ret = pthread_rwlock_wrlock(radioServiceRwlockPtr); 797 assert(ret == 0); 798 //Convert the RadioResponse instance and RadioIndication instance of java layer into the instance of c + + layer 799 mRadioResponse = radioResponseParam; 800 mRadioIndication = radioIndicationParam; 801 mRadioResponseV1_1 = V1_1::IRadioResponse::castFrom(mRadioResponse).withDefault(nullptr); 802 mRadioIndicationV1_1 = V1_1::IRadioIndication::castFrom(mRadioIndication).withDefault(nullptr); 803 if (mRadioResponseV1_1 == nullptr || mRadioIndicationV1_1 == nullptr) { 804 mRadioResponseV1_1 = nullptr; 805 mRadioIndicationV1_1 = nullptr; 806 } 807 //mSlotId ++ 808 mCounterRadio[mSlotId]++; 809 812 813 // client is connected. Send initial indications. 814 android::onNewCommandConnect((RIL_SOCKET_ID) mSlotId); 815 816 return Void(); 817}
android. hardware. radio@1.2 -Radio service this halService will set some response functions when starting, and the corresponding relationship is in ril_unsol_commands.h, where the repose of the time and time zone sent by the operator is RIL_UNSOL_NITZ_TIME_RECEIVED, the corresponding function is radio::nitzTimeReceivedInd,
/hardware/ril/libril/ril_unsol_commands.h {RIL_UNSOL_NITZ_TIME_RECEIVED, radio::nitzTimeReceivedInd, WAKE_PARTIAL},
When hal servce receives some responses, it will call the following function: RIL_onUnsolicitedResponse:
/hardware/ril/libril/ril.cpp 722#if defined(ANDROID_MULTI_SIM) 723extern "C" 724void RIL_onUnsolicitedResponse(int unsolResponse, const void *data, 725 size_t datalen, RIL_SOCKET_ID socket_id) 726#else 727extern "C" 728void RIL_onUnsolicitedResponse(int unsolResponse, const void *data, 729 size_t datalen) 730#endif 731{ 732 int unsolResponseIndex; 733 int ret; //Determine in RIL according to the response code_ unsol_ commands. H location 748 unsolResponseIndex = unsolResponse - RIL_UNSOL_RESPONSE_BASE; 749 750 if ((unsolResponseIndex < 0) 751 || (unsolResponseIndex >= (int32_t)NUM_ELEMS(s_unsolResponses))) { 752 RLOGE("unsupported unsolicited response code %d", unsolResponse); 753 return; 754 } 755 756 // Grab a wake lock if needed for this reponse, 757 // as we exit we'll either release it immediately 758 // or set a timer to release it later. 759 switch (s_unsolResponses[unsolResponseIndex].wakeType) { 760 case WAKE_PARTIAL: 761 grabPartialWakeLock(); 762 shouldScheduleTimeout = true; 763 break; 764 765 case DONT_WAKE: 766 default: 767 // No wake lock is grabed so don't set timeout 768 shouldScheduleTimeout = false; 769 break; 770 } 771 772 appendPrintBuf("[UNSL]< %s", requestToString(unsolResponse)); 773 784 785 if (unsolResponse == RIL_UNSOL_NITZ_TIME_RECEIVED) { 786 // get a write lock in caes of NITZ since setNitzTimeReceived() is called 787 rwlockRet = pthread_rwlock_wrlock(radioServiceRwlockPtr); 788 assert(rwlockRet == 0); //Set the current running time 789 radio::setNitzTimeReceived((int) soc_id, android::elapsedRealtime()); 790 } else { 791 rwlockRet = pthread_rwlock_rdlock(radioServiceRwlockPtr); 792 assert(rwlockRet == 0); 793 } 794 //Call radio::nitzTimeReceivedInd to process RIL_UNSOL_NITZ_TIME_RECEIVED 795 ret = s_unsolResponses[unsolResponseIndex].responseFunction( 796 (int) soc_id, responseType, 0, RIL_E_SUCCESS, const_cast<void*>(data), 797 datalen); .... 817 }
/hardware/ril/libril/ril_service.cpp 8550void radio::setNitzTimeReceived(int slotId, long timeReceived) { 8551 nitzTimeReceived[slotId] = timeReceived; 8552} 6983int radio::nitzTimeReceivedInd(int slotId, 6984 int indicationType, int token, RIL_Errno e, void *response, 6985 size_t responseLen) { 6986 if (radioService[slotId] != NULL && radioService[slotId]->mRadioIndication != NULL) { 6987 if (response == NULL || responseLen == 0) { 6988 RLOGE("nitzTimeReceivedInd: invalid response"); 6989 return 0; 6990 } 6991 hidl_string nitzTime = convertCharPtrToHidlString((char *) response); 6992#if VDBG 6993 RLOGD("nitzTimeReceivedInd: nitzTime %s receivedTime %" PRId64, nitzTime.c_str(), 6994 nitzTimeReceived[slotId]); 6995#endif //Call back to the nitzTimeReceived function of RadioIndication in the java layer, where nitzTime is the time and time zone information returned by the operator, and nitzTimeReceived is the running time of the system 6996 Return<void> retStatus = radioService[slotId]->mRadioIndication->nitzTimeReceived( 6997 convertIntToRadioIndicationType(indicationType), nitzTime, 6998 nitzTimeReceived[slotId]); 6999 radioService[slotId]->checkReturnStatus(retStatus); 7000 } else { 7001 RLOGE("nitzTimeReceivedInd: radioService[%d]->mRadioIndication == NULL", slotId); 7002 return -1; 7003 } 7004 7005 return 0; 7006}
Therefore, the key to the whole process is that the RIL instance will set a callback to the hal service during initialization, so that when the hal service receives the operator's time and time zone information, it can be returned to the Java layer for processing and setting the system time and time zone. The callback function for processing time and time zone information is the nitzTimeReceived function of RadioIndication.
/frameworks/opt/telephony/src/java/com/android/internal/telephony/RadioIndication.java 202 public void nitzTimeReceived(int indicationType, String nitzTime, long receivedTime) { 203 mRil.processIndication(indicationType); 204 205 if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime); 206 //Put the time zone information obtained from the native layer and the running time of the system when obtaining the time zone information in an Object array 207 // todo: Clean this up with a parcelable class for better self-documentation 208 Object[] result = new Object[2]; 209 result[0] = nitzTime; 210 result[1] = receivedTime; 211 212 boolean ignoreNitz = SystemProperties.getBoolean( 213 TelephonyProperties.PROPERTY_IGNORE_NITZ, false); 214 215 if (ignoreNitz) { 216 if (RIL.RILJ_LOGD) mRil.riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED"); 217 } else { 218 if (mRil.mNITZTimeRegistrant != null) { //Call MRIL mNITZTimeRegistrant. Notifyregistry process and set 219 mRil.mNITZTimeRegistrant.notifyRegistrant(new AsyncResult (null, result, null)); 220 } 221 // in case NITZ time registrant isn't registered yet, or a new registrant 222 // registers later 223 mRil.mLastNITZTimeInfo = result; 224 } 225 }
mRil here is an instance of RIL. Where is mnitztimeregister initialized? We need to go back to the operator. Before sending the time and time zone information, when the RIL instance obtains the remote interface of halservice and sets the RadioResponse instance and RadioIndication instance, the RIL instantiation ends. At this time, we will take the following second step:
2. Create the processing function of binding time zone information of GsmCdmaPhone instance with RIL instance as parameter
/frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaPhone.java 211 public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, 212 boolean unitTestMode, int phoneId, int precisePhoneType, 213 TelephonyComponentFactory telephonyComponentFactory) { ... //makeServiceStateTracker will create a thread handler to process the time and time zone information returned by the operator 225 mSST = mTelephonyComponentFactory.makeServiceStateTracker(this, this.mCi); ... 232 logd("GsmCdmaPhone: constructor: sub = " + mPhoneId); 233 } /frameworks/opt/telephony/src/java/com/android/internal/telephony/TelephonyComponentFactory.java 64 public ServiceStateTracker makeServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) { 65 return new ServiceStateTracker(phone, ci); 66 } /frameworks/opt/telephony/src/java/com/android/internal/telephony/ServiceStateTracker.java 487 public ServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) { //makeNitzStateMachine will create a new NitzStateMachine, then initialize TimeServiceHelper and call setListener to register the database to listen to Part 4 488 mNitzState = TelephonyComponentFactory.getInstance().makeNitzStateMachine(phone); 489 mPhone = phone; 490 mCi = ci; 491 ... 517 // Create a new handler thread dedicated for locale tracker because the blocking 518 // getAllCellInfo call requires clients calling from a different thread. //Start a thread 519 mHandlerThread = new HandlerThread(LocaleTracker.class.getSimpleName()); 520 mHandlerThread.start(); ... //Binding processing functions for processing time and time zone information 526 mCi.setOnNITZTime(this, EVENT_NITZ_TIME, null); 527 ... 569 }
From the above code, we know that mCi is an instance of RIL, so it will call RIL setOnNITZTime function of Java:
/frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java 492 @Override 493 public void setOnNITZTime(Handler h, int what, Object obj) { 494 super.setOnNITZTime(h, what, obj); 495 //If it is the first time to start mLastNITZTimeInfo == null 496 // Send the last NITZ time if we have it 497 if (mLastNITZTimeInfo != null) { 498 mNITZTimeRegistrant 499 .notifyRegistrant( 500 new AsyncResult (null, mLastNITZTimeInfo, null)); 501 } 502 }
You can see that setOnNITZTime and RIL are called to the parent class The parent class of Java is basecommands java
/frameworks/opt/telephony/src/java/com/android/internal/telephony/BaseCommands.java 376 @Override 377 public void setOnNITZTime(Handler h, int what, Object obj) { //what is EVENT_NITZ_TIME h refers to ServiceStateTracker 378 mNITZTimeRegistrant = new Registrant (h, what, obj); 379 } /frameworks/base/core/java/android/os/Registrant.java public 28 Registrant(Handler h, int what, Object obj) 29 { 30 refH = new WeakReference(h); 31 this.what = what; 32 userObj = obj; 33 }
At this point, we return to the nitzTimeReceived function of RadioIndication, and finally call mRil.. mNITZTimeRegistrant. notifyRegistrant(new AsyncResult (null, result, null));
Mnitztimeregistry is what is event_ NITZ_ The time registry and notifyregistry are as follows:
/frameworks/base/core/java/android/os/Registrant.java 48 public void 49 notifyResult(Object result) 50 { 51 internalNotifyRegistrant (result, null); 52 } 69 /*package*/ void 70 internalNotifyRegistrant (Object result, Throwable exception) 71 { 72 Handler h = getHandler(); 73 74 if (h == null) { 75 clear(); 76 } else { 77 Message msg = Message.obtain(); 78 79 msg.what = what; 80 81 msg.obj = new AsyncResult(userObj, result, exception); 82 83 h.sendMessage(msg); 84 } 85 }
Therefore, the time and time zone information sent by the operator will be handed over to the ServiceStateTracker handler for processing
994 @Override 995 public void handleMessage(Message msg) { 996 AsyncResult ar; 997 int[] ints; 998 Message message; ... 1176 case EVENT_NITZ_TIME: 1177 ar = (AsyncResult) msg.obj; 1178 //Get the time and time zone information from the message 1179 String nitzString = (String)((Object[])ar.result)[0]; 1180 long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue(); 1181 //Set time and time zone information 1182 setTimeFromNITZString(nitzString, nitzReceiveTime); 1183 break;
3. Processing time zone information
The processing of time and time zone information sent by operators is mainly carried out by calling setTimeFromNITZString function as follows:
/frameworks/opt/telephony/src/java/com/android/internal/telephony/ServiceStateTracker.java 3537 private void setTimeFromNITZString(String nitzString, long nitzReceiveTime) { //start the running time of the current system 3538 long start = SystemClock.elapsedRealtime(); 3539 if (DBG) { 3540 Rlog.d(LOG_TAG, "NITZ: " + nitzString + "," + nitzReceiveTime 3541 + " start=" + start + " delay=" + (start - nitzReceiveTime)); 3542 } //Convert the time zone and time information sent by the operator into NitzData entity class 3543 NitzData newNitzData = NitzData.parse(nitzString); 3544 if (newNitzData != null) { 3545 try { //Then the NitzData and the information received by the operator will be combined into TimeStampedValue and the handleNitzReceived will be set up. 3546 TimeStampedValue<NitzData> nitzSignal = 3547 new TimeStampedValue<>(newNitzData, nitzReceiveTime); 3548 mNitzState.handleNitzReceived(nitzSignal); 3549 } finally { 3550 if (DBG) { 3551 long end = SystemClock.elapsedRealtime(); 3552 Rlog.d(LOG_TAG, "NITZ: end=" + end + " dur=" + (end - start)); 3553 } 3554 } 3555 } 3556 } /frameworks/opt/telephony/src/java/com/android/internal/telephony/NitzStateMachine.java 384 public void handleNitzReceived(TimeStampedValue<NitzData> nitzSignal) { 385 handleTimeZoneFromNitz(nitzSignal); 386 handleTimeFromNitz(nitzSignal); 387 }
Then, handleTimeZoneFromNitz and handleTimeFromNitz are called to process the time and time zone information respectively.
3.1 time zone information processing handleTimeZoneFromNitz
/frameworks/opt/telephony/src/java/com/android/internal/telephony/NitzStateMachine.java 389 private void handleTimeZoneFromNitz(TimeStampedValue<NitzData> nitzSignal) { 390 //A series of processing 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) { //Whether the time zone is automatically obtained in setting 439 if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) { //Set time and send broadcast 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 } 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 } /frameworks/opt/telephony/src/java/com/android/internal/telephony/TimeServiceHelper.java 129 public boolean isTimeZoneDetectionEnabled() { 130 try { 131 return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE) > 0; 132 } catch (Settings.SettingNotFoundException snfe) { 133 return true; 134 } 135 } 143 public void setDeviceTimeZone(String zoneId) { 144 setDeviceTimeZoneStatic(mContext, zoneId); 145 } 185 static void setDeviceTimeZoneStatic(Context context, String zoneId) { 186 AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 187 alarmManager.setTimeZone(zoneId); 188 Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE); 189 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 190 intent.putExtra("time-zone", zoneId); 191 context.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 192 }
You can see that the last time zone information is set in the alarmManager, and the broadcast sent is Android intent. action. NETWORK_ SET_ TIMEZONE
3.2 processing of time information handleTimeFromNitz
/frameworks/opt/telephony/src/java/com/android/internal/telephony/NitzStateMachine.java 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(); //1. Calculate the elapsed time from the message received from the operator by the halservice to the current system 473 long millisSinceNitzReceived = elapsedRealtime - nitzSignal.mElapsedRealtime; 474 if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) { 475 //Boundary treatment 480 return; 481 } 482 483 // Adjust the NITZ time by the delay since it was received to get the time now. //2. Calculate the current absolute time 484 long adjustedCurrentTimeMillis = 485 nitzSignal.mValue.getCurrentTimeInMillis() + millisSinceNitzReceived; //3. Calculate the difference between network time and system time 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 //If it is the first update time 495 if (mSavedNitzTime == null) { 496 logMsg += ": First update received."; //Set the time and send the broadcast 497 setAndBroadcastNetworkSetTime(logMsg, adjustedCurrentTimeMillis); 498 } else { //Calculate the running time of the system from the last save to the present 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 //Save the current time information and system operation time information 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 } 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 } /frameworks/opt/telephony/src/java/com/android/internal/telephony/TimeServiceHelper.java 153 public void setDeviceTime(long time) { 154 SystemClock.setCurrentTimeMillis(time); 155 Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME); 156 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 157 intent.putExtra("time", time); 158 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 159 } /frameworks/base/core/java/android/os/SystemClock.java 149 public static boolean setCurrentTimeMillis(long millis) { 150 final IAlarmManager mgr = IAlarmManager.Stub 151 .asInterface(ServiceManager.getService(Context.ALARM_SERVICE)); 152 if (mgr == null) { 153 return false; 154 } 155 156 try { 157 return mgr.setTime(millis); 158 } catch (RemoteException e) { 159 Slog.e(TAG, "Unable to set RTC", e); 160 } catch (SecurityException e) { 161 Slog.e(TAG, "Unable to set RTC", e); 162 } 163 164 return false; 165 }
You can see that the time is finally set in SystemClock, which is actually set in AlarmManagerService.
4. Start the process of automatically updating time and time zone in setting
The Setting switch will change the settings in the Setting database Global. AUTO_ TIME_ Zone and settings Global. AUTO_ TIME_ The value of zone. When creating and processing handler ServiceStateTracker in the second stage, a database listener will be registered, as follows:
/frameworks/opt/telephony/src/java/com/android/internal/telephony/ServiceStateTracker.java 487 public ServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) { //makeNitzStateMachine will create a new NitzStateMachine, then initialize TimeServiceHelper and call setListener to register the database to listen to Part 4 488 mNitzState = TelephonyComponentFactory.getInstance().makeNitzStateMachine(phone); 489 mPhone = phone; 490 mCi = ci; 491 ... 517 // Create a new handler thread dedicated for locale tracker because the blocking 518 // getAllCellInfo call requires clients calling from a different thread. //Start a thread 519 mHandlerThread = new HandlerThread(LocaleTracker.class.getSimpleName()); 520 mHandlerThread.start(); ... //Binding processing functions for processing time and time zone information 526 mCi.setOnNITZTime(this, EVENT_NITZ_TIME, null); 527 ... 569 } /frameworks/opt/telephony/src/java/com/android/internal/telephony/TelephonyComponentFactory.java 71 public NitzStateMachine makeNitzStateMachine(GsmCdmaPhone phone) { 72 return new NitzStateMachine(phone); 73 } /frameworks/opt/telephony/src/java/com/android/internal/telephony/NitzStateMachine.java 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; //Create TimeServiceHelper and set listening 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 } /frameworks/opt/telephony/src/java/com/android/internal/telephony/TimeServiceHelper.java 69 public void setListener(Listener listener) { 70 if (listener == null) { 71 throw new NullPointerException("listener==null"); 72 } 73 if (mListener != null) { 74 throw new IllegalStateException("listener already set"); 75 } 76 this.mListener = listener; //Register database listening 77 mCr.registerContentObserver( 78 Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true, 79 new ContentObserver(new Handler()) { 80 public void onChange(boolean selfChange) { 81 listener.onTimeDetectionChange(isTimeDetectionEnabled()); 82 } 83 }); 84 mCr.registerContentObserver( 85 Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true, 86 new ContentObserver(new Handler()) { 87 public void onChange(boolean selfChange) { 88 listener.onTimeZoneDetectionChange(isTimeZoneDetectionEnabled()); 89 } 90 }); 91 }
As you can see from the code, when the settings in the database Global. AUTO_ Time and settings Global. AUTO_ TIME_ When the zone changes, it will call onTimeDetectionChange and onTimeZoneDetectionChange, and then call back to handleAutoTimeEnabled and handleAutoTimeZoneEnabled to process automatic time acquisition and automatic time zone acquisition respectively,
/frameworks/opt/telephony/src/java/com/android/internal/telephony/NitzStateMachine.java 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 }
You can see that setAndBroadcastNetworkSetTime and setAndBroadcastNetworkSetTimeZone are still called to update the time, but they can only be updated when mSavedNitzTime and mSavedTimeZoneId are set. Where are mSavedNitzTime and mSavedTimeZoneId set? A global search of these two variables shows that they are set in the following places: they will be set when NITZ is updated
/frameworks/opt/telephony/src/java/com/android/internal/telephony/NitzStateMachine.java private void handleTimeZoneFromNitz(TimeStampedValue<NitzData> nitzSignal) { 438 if (zoneId != null) { 439 if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) { 440 setAndBroadcastNetworkSetTimeZone(zoneId); 441 } 442 mNitzTimeZoneDetectionSuccessful = true; 443 mSavedTimeZoneId = zoneId; 444 } } private void handleTimeFromNitz(TimeStampedValue<NitzData> nitzSignal) { 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 } }
NTP time update mechanism
flow chart:
1. Startup process of networktimeupdateservice
NTP time update is initialized and set in NetworkTimeUpdateService. NetworkTimeUpdateService is started in SystemServer, as follows:
/frameworks/base/services/java/com/android/server/SystemServer.java 724 private void startOtherServices() { ... 736 NetworkTimeUpdateService networkTimeUpdater = null; ... //isWatch is a watch device and is true 1402 if (!isWatch) { 1403 traceBeginAndSlog("StartNetworkTimeUpdateService"); 1404 try { 1405 networkTimeUpdater = new NetworkTimeUpdateService(context); 1406 ServiceManager.addService("network_time_update_service", networkTimeUpdater); 1407 } catch (Throwable e) { 1408 reportWtf("starting NetworkTimeUpdate service", e); 1409 } 1410 traceEnd(); ... 1867 traceBeginAndSlog("MakeNetworkTimeUpdateReady"); 1868 try { 1869 if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning(); 1870 } catch (Throwable e) { 1871 reportWtf("Notifying NetworkTimeService running", e); 1872 } 1873 traceEnd(); }
1.1 initialization of networktimeupdateservice:
/frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java 101 public NetworkTimeUpdateService(Context context) { 102 mContext = context; //Initialize NtpTrustedTime 103 mTime = NtpTrustedTime.getInstance(context); 104 mAlarmManager = mContext.getSystemService(AlarmManager.class); 105 mCM = mContext.getSystemService(ConnectivityManager.class); 106 107 Intent pollIntent = new Intent(ACTION_POLL, null); 108 mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0); 109 //Some parameters about NTP 110 mPollingIntervalMs = mContext.getResources().getInteger( 111 com.android.internal.R.integer.config_ntpPollingInterval); 112 mPollingIntervalShorterMs = mContext.getResources().getInteger( 113 com.android.internal.R.integer.config_ntpPollingIntervalShorter); 114 mTryAgainTimesMax = mContext.getResources().getInteger( 115 com.android.internal.R.integer.config_ntpRetry); 116 mTimeErrorThresholdMs = mContext.getResources().getInteger( 117 com.android.internal.R.integer.config_ntpThreshold); 118 119 mWakeLock = context.getSystemService(PowerManager.class).newWakeLock( 120 PowerManager.PARTIAL_WAKE_LOCK, TAG); 121 } /frameworks/base/core/java/android/util/NtpTrustedTime.java 59 public static synchronized NtpTrustedTime getInstance(Context context) { 60 if (sSingleton == null) { 61 final Resources res = context.getResources(); 62 final ContentResolver resolver = context.getContentResolver(); 63 //The defaultServer is time android. com 64 final String defaultServer = res.getString( 65 com.android.internal.R.string.config_ntpServer); //The defaultTimeout is 5000 66 final long defaultTimeout = res.getInteger( 67 com.android.internal.R.integer.config_ntpTimeout); 68 69 final String secureServer = Settings.Global.getString( 70 resolver, Settings.Global.NTP_SERVER); 71 final long timeout = Settings.Global.getLong( 72 resolver, Settings.Global.NTP_TIMEOUT, defaultTimeout); 73 74 final String server = secureServer != null ? secureServer : defaultServer; 75 sSingleton = new NtpTrustedTime(server, timeout); 76 sContext = context; 77 } 78 79 return sSingleton; 80 }
NetworkTimeUpdateService obtains a single instance of NtpTrustedTime during initialization. NtpTrustedTime is the implementation class of NTP.
1.2 startup of networktimeupdateservice:
/frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java 123 /** Initialize the receivers and initiate the first NTP request */ 124 public void systemRunning() { //1. Register some broadcast recipients, such as "android.intent.action.NETWORK_SET_TIME" and "com.android.server.NetworkTimeUpdateService.action.POLL" 125 registerForTelephonyIntents(); 126 registerForAlarms(); 127 //2. Start the worker thread and set the handler 128 HandlerThread thread = new HandlerThread(TAG); 129 thread.start(); 130 mHandler = new MyHandler(thread.getLooper()); //3. Register a callback function to listen for changes in network status 131 mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback(); 132 mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler); 133 //4. Register the function to monitor the change of Setting database 134 mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED); 135 mSettingsObserver.observe(mContext); 136 }
If NITZ updates the time successfully and sends ACTION_NETWORK_SET_TIME, then the mNitzReceiver of NetworkTimeUpdateService will receive this broadcast and set mNitzTimeSetTime as follows:
243 /** Receiver for Nitz time events */ 244 private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() { 245 246 @Override 247 public void onReceive(Context context, Intent intent) { 248 String action = intent.getAction(); 249 if (DBG) Log.d(TAG, "Received " + action); 250 if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) { 251 mNitzTimeSetTime = SystemClock.elapsedRealtime(); 252 } 253 } 254 };
If mNitzTimeSetTime is not set, the initial value is NOT_SET = -1;
2.NTP time update
There are three kinds of NTP time update triggers
2.1 enable automatic time acquisition in Settings
When NITZ does not take effect, if the Internet is connected and automatic time setting is enabled, the following process will be followed
Enabling automatic time acquisition in Setting will trigger the callback function onChange of database listening:
/frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java 291 private static class SettingsObserver extends ContentObserver { 292 293 private int mMsg; 294 private Handler mHandler; 295 296 SettingsObserver(Handler handler, int msg) { 297 super(handler); 298 mHandler = handler; 299 mMsg = msg; 300 } 301 302 void observe(Context context) { 303 ContentResolver resolver = context.getContentResolver(); 304 resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME), 305 false, this); 306 } 307 308 @Override 309 public void onChange(boolean selfChange) { 310 mHandler.obtainMessage(mMsg).sendToTarget(); 311 } 312 }
The msg passed in during the previous registration is EVENT_AUTO_TIME_CHANGED, handler is the worker thread of NetworkTimeUpdateService, handler Myhandler:
/frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java 256 /** Handler to do the network accesses on */ 257 private class MyHandler extends Handler { 258 259 public MyHandler(Looper l) { 260 super(l); 261 } 262 263 @Override 264 public void handleMessage(Message msg) { 265 switch (msg.what) { 266 case EVENT_AUTO_TIME_CHANGED: 267 case EVENT_POLL_NETWORK_TIME: 268 case EVENT_NETWORK_CHANGED: 269 onPollNetworkTime(msg.what); 270 break; 271 } 272 } 273 } 154 private void onPollNetworkTime(int event) { 155 // If Automatic time is not set, don't bother. Similarly, if we don't 156 // have any default network, don't bother. //mDefaultNetwork is connected to the network. If it is not connected to the network, it returns directly 157 if (mDefaultNetwork == null) return; 158 mWakeLock.acquire(); 159 try { 160 onPollNetworkTimeUnderWakeLock(event); 161 } finally { 162 mWakeLock.release(); 163 } 164 } 166 private void onPollNetworkTimeUnderWakeLock(int event) { 167 // Force an NTP fix when outdated //mTime here refers to ntptrustedtime. Getcacheage returns the difference between the time when NTP was last saved and the current time. If NTP time has not been obtained, the maximum value of Integer is returned 168 if (mTime.getCacheAge() >= mPollingIntervalMs) { 169 if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh"); //Update time 170 mTime.forceRefresh(); 171 } 172 173 if (mTime.getCacheAge() < mPollingIntervalMs) { 174 // Obtained fresh fix; schedule next normal update //Reset updated timer 175 resetAlarm(mPollingIntervalMs); //isAutomaticTimeRequested returns whether the Setting database is enabled to automatically update the time. Here, true is returned 176 if (isAutomaticTimeRequested()) { 177 updateSystemClock(event); 178 } 179 180 } else { 181 // No fresh fix; schedule retry 182 mTryAgainCounter++; 183 if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) { 184 resetAlarm(mPollingIntervalShorterMs); 185 } else { 186 // Try much later 187 mTryAgainCounter = 0; 188 resetAlarm(mPollingIntervalMs); 189 } 190 } 191 } 205 private void updateSystemClock(int event) { 206 final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED); 207 if (!forceUpdate) { //If the time is not automatically updated, the triggered time update will go below //getNitzAge returns systemclock Elapseddrealtime() - the value of mnitztimesettime. If NITZ is invalid, the maximum value will be returned //If NITZ has just been updated, NTP does not need to be updated and returns directly 208 if (getNitzAge() < mPollingIntervalMs) { 209 if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ"); 210 return; 211 } 212 213 final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis()); 214 if (skew < mTimeErrorThresholdMs) { 215 if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew"); 216 return; 217 } 218 } 219 //Finally set into the system 220 SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis()); 221 }
Therefore, the update core of NTP time function is mtime forceRefresh()
/frameworks/base/core/java/android/util/NtpTrustedTime.java 95 public boolean forceRefresh(Network network) { //The server here is settings in the Setting database Global. NTP_ Server or time android. com 96 if (TextUtils.isEmpty(mServer)) { 97 // missing server, so no trusted time available 98 return false; 99 } 100 //Get network services 101 // We can't do this at initialization time: ConnectivityService might not be running yet. 102 synchronized (this) { 103 if (mCM == null) { 104 mCM = sContext.getSystemService(ConnectivityManager.class); 105 } 106 } 107 //Get connected network 108 final NetworkInfo ni = mCM == null ? null : mCM.getNetworkInfo(network); 109 if (ni == null || !ni.isConnected()) { 110 if (LOGD) Log.d(TAG, "forceRefresh: no connectivity"); 111 return false; 112 } 113 114 115 if (LOGD) Log.d(TAG, "forceRefresh() from cache miss"); 116 final SntpClient client = new SntpClient(); //Call the requestTime of SntpClient and request the time from the server through the socket 117 if (client.requestTime(mServer, (int) mTimeout, network)) { 118 mHasCache = true; 119 mCachedNtpTime = client.getNtpTime(); 120 mCachedNtpElapsedRealtime = client.getNtpTimeReference(); 121 mCachedNtpCertainty = client.getRoundTripTime() / 2; 122 return true; 123 } else { 124 return false; 125 } 126 }
2.2 network connection
If automatic time acquisition has been enabled, when the network is connected, it will call back to onAvailable of NetworkTimeUpdateCallback. You can see that onPollNetworkTime also goes, but the incoming Event is EVENT_NETWORK_CHANGED
/frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java 275 private class NetworkTimeUpdateCallback extends NetworkCallback { 276 @Override 277 public void onAvailable(Network network) { 278 Log.d(TAG, String.format("New default network %s; checking time.", network)); 279 mDefaultNetwork = network; 280 // Running on mHandler so invoke directly. 281 onPollNetworkTime(EVENT_NETWORK_CHANGED); 282 } 283 284 @Override 285 public void onLost(Network network) { //If the network is disconnected, mDefaultNetwork is set to null 286 if (network.equals(mDefaultNetwork)) mDefaultNetwork = null; 287 } 288 }
2.3 when it is time to update:
NetworkTimeUpdateService registers a broadcast receiver when the system starts. When receiving the action_ When poll, it will call back to the following functions:
/frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java 144 private void registerForAlarms() { 145 mContext.registerReceiver( 146 new BroadcastReceiver() { 147 @Override 148 public void onReceive(Context context, Intent intent) { //Where mHandler is Myhandler 149 mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget(); 150 } 151 }, new IntentFilter(ACTION_POLL)); 152 } 256 /** Handler to do the network accesses on */ 257 private class MyHandler extends Handler { 258 259 public MyHandler(Looper l) { 260 super(l); 261 } 262 263 @Override 264 public void handleMessage(Message msg) { 265 switch (msg.what) { 266 case EVENT_AUTO_TIME_CHANGED: 267 case EVENT_POLL_NETWORK_TIME: 268 case EVENT_NETWORK_CHANGED: 269 onPollNetworkTime(msg.what); 270 break; 271 } 272 } 273 }
You can see that you have finally reached onPollNetworkTime; The rest are the same. When the device is powered on, it will call resetAlarm to reset the timer after updating the time through NTP for the first time;
At this time, a timer will be triggered and the broadcast will be sent after the time
228 private void resetAlarm(long interval) { 229 mAlarmManager.cancel(mPendingPollIntent); 230 long now = SystemClock.elapsedRealtime(); 231 long next = now + interval; 232 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent); 233 }
NITZ and NTP summary
Now there are two ways for Android to synchronize time through the network: NITZ and NTP. They use different conditions and can obtain different information; After checking the automatic synchronization function, the phone will first try NITZ mode. If the time acquisition fails, NTP mode will be used
1.NITZ(network identity and time zone) synchronization time
NITZ is a GSM/WCDMA base station mode. SIM card must be inserted and supported by the operator; Time and time zone information can be provided
Chinese mainland operators basically do not support it.
2.NTP(network time protocol) synchronization time
NTP is used when there is no SIM card or the operator does not support NITZ. It only obtains the time through the network (GPRS/WIFI), and only provides time information without time zone information (therefore, in areas that do not support NITZ, the function of automatically obtaining time zone is actually invalid)
NTP also has a caching mechanism: the current time successfully obtained will be saved. When the user turns on the automatic time update function next time, the time will be updated in combination with the mobile phone clock. This is also the reason why the mobile phone can automatically update the time when there is no network.