Android9.0 本地时区和本地时间的自动更新机制

简介

现在Android通过网络同步时间有两种方式:NITZ和NTP,它们使用的条件不同,可以获取的信息也不一样;勾选自动同步功能后,手机首先会尝试NITZ方式,若获取时间失败,则使用NTP方式
1.NITZ(network identity and time zone)同步时间

​ NITZ是一种GSM/WCDMA基地台方式,必须插入SIM卡;可以提供时间和时区信息

2.NTP(network time protocol)同步时间

​ NTP在无SIM卡或operator不支持NITZ时使用,单纯通过网络(GPRS/WIFI)获取时间,只提供时间信息,没有时区信息(因此在不支持NITZ的地区,自动获取时区功能实际上是无效的)

​ NTP还有一种缓存机制:当前成功获取的时间会保存下来,当用户下次开启自动更新时间功能时会结合手机clock来进行时间更新。这也是没有任何网络时手机却能自动更新时间的原因。

NITZ时间更新的流程:

SIM卡上报信息,运营商提供时区信息和时间信息给终端更新

流程图:

需要com.android.phone进程启动当android.intent.action.BOOT_COMPLETED广播发出来时,com.android.phone进程就会启动,同时调用Application的onCreate,com.android.phone进程的Application类是PhoneApp.java如下:

/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    }

可以看到onCreate初始化了两个全局类PhoneGlobals和TelephonyGlobals并调用其onCreate函数,时间的自动更新则是在PhoneGlobals的onCreate中完成的

/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 *///获取sim卡的个数
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    }

上面这个函数会根据sim卡的个数去创建RIL实例,RIL实例的主要作用是和halsevice 建立联系并设置一些回调给halservice android.hardware.radio@1.2-radio-service 其实现为ril_service.cpp。接着以RIL实例为参数创建了GsmCdmaPhone主要是为了绑定处理hal层传过来的时间和时区信息的函数。下面分别看这两步的具体细节:

1.创建RIL实例 设置回调给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            //不支持手机移动网络
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());//将RIL实例化时的RadioResponse实例和RadioIndication实例设置给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    }

之前说过RIL获取的hal Service的实现是ril_service.cpp所以会调用到ril_service的setResponseFunctions:

/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    //将java层的RadioResponse实例和RadioIndication实例转换成c++层的实例
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 这个halService 在启动时会设置一些回应函数其对应关系在ril_unsol_commands.h中其中运营商发送的时间和时区的repose是RIL_UNSOL_NITZ_TIME_RECEIVED,对应的函数则是radio::nitzTimeReceivedInd,

/hardware/ril/libril/ril_unsol_commands.h
{RIL_UNSOL_NITZ_TIME_RECEIVED, radio::nitzTimeReceivedInd, WAKE_PARTIAL},

当hal servce接受到某些回应时会调用到下面这个函数: 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;//根据回应码确定在ril_unsol_commands.h的位置
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);//设置当前的运行时间
789        radio::setNitzTimeReceived((int) soc_id, android::elapsedRealtime());
790    } else {
791        rwlockRet = pthread_rwlock_rdlock(radioServiceRwlockPtr);
792        assert(rwlockRet == 0);
793    }
794    //调用radio::nitzTimeReceivedInd处理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 //回调到java层的RadioIndication的nitzTimeReceived函数中 其中nitzTime为运营商返回的时间和时区信息nitzTimeReceived则是系统运行的时长
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}

所以整个流程的关键就是RIL实例在初始化时会设置回调给hal Service以便于在hal service 接收到运营商的时间和时区信息时能回传到Java层用来处理并设置系统的时间和时区。而这个处理时间和时区信息的回调函数就是RadioIndication的 nitzTimeReceived函数。

/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        //将从native层得到的时间时区信息和得到时间时区信息时系统的运行时间放在一个Object数组
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) {//调用mRil.mNITZTimeRegistrant.notifyRegistrant处理并设置
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就是ril实例,那mNITZTimeRegistrant是在哪里初始化的呢?需要我们回到运营商在发送时间和时区信息之前,当RIL实例在获取到halservice的远程接口并设置完RadioResponse实例和RadioIndication实例后,RIL的实例化就结束了,这时会走下面的第二步:

2.以RIL实例为参数创建GsmCdmaPhone实例 绑定时间时区信息的处理函数

 /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会创建一个线程handler来处理运营商返回的时间和时区信息
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会新建一个NitzStateMachine 接着初始化TimeServiceHelper并调用setListener注册数据库监听 第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.//开启一个线程
519        mHandlerThread = new HandlerThread(LocaleTracker.class.getSimpleName());
520        mHandlerThread.start();...//绑定处理时间和时区信息的处理函数
526        mCi.setOnNITZTime(this, EVENT_NITZ_TIME, null);
527...
569    }

从上面的代码我们知道mCi是RIL的实例所以会调用到RIL.java的setOnNITZTime函数:

/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        //如果是第一次启动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    }

可以看到是调用到父类的setOnNITZTime,RIL.java的父类是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是EVENT_NITZ_TIME h指的是ServiceStateTracker
378        mNITZTimeRegistrant = new Registrant (h, what, obj);
379    }
/frameworks/base/core/java/android/os/Registrant.javapublic
28    Registrant(Handler h, int what, Object obj)
29    {30        refH = new WeakReference(h);
31        this.what = what;
32        userObj = obj;
33    }

这时我们回到RadioIndication的 nitzTimeReceived函数,最后调用的是 mRil.mNITZTimeRegistrant.notifyRegistrant(new AsyncResult (null, result, null));

mNITZTimeRegistrant就是what为EVENT_NITZ_TIME的Registrant,notifyRegistrant如下:

/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    }

所以最后运营商发送的时间和时区的信息会交给ServiceStateTracker这个handler去处理

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                //从message中取出时间和时区信息
1179                String nitzString = (String)((Object[])ar.result)[0];
1180                long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue();
1181                //设置时间和时区信息
1182                setTimeFromNITZString(nitzString, nitzReceiveTime);
1183                break;

3.处理时间时区信息

对于运营商发送的时间和时区信息的处理主要是调用setTimeFromNITZString函数来进行的如下:

/frameworks/opt/telephony/src/java/com/android/internal/telephony/ServiceStateTracker.java
3537    private void setTimeFromNITZString(String nitzString, long nitzReceiveTime) {//start 当前系统运行的时间
3538        long start = SystemClock.elapsedRealtime();
3539        if (DBG) {3540            Rlog.d(LOG_TAG, "NITZ: " + nitzString + "," + nitzReceiveTime
3541                    + " start=" + start + " delay=" + (start - nitzReceiveTime));
3542        }//将运营商发送的时区和时间信息转换成NitzData实体类
3543        NitzData newNitzData = NitzData.parse(nitzString);
3544        if (newNitzData != null) {3545            try {//再将NitzData和接收到运营商的信息时系统的运行时间组合成TimeStampedValue 最后调用handleNitzReceived进行设置
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    }

接着调用handleTimeZoneFromNitz和handleTimeFromNitz分别处理时间和时区信息。

3.1时区信息处理 handleTimeZoneFromNitz

/frameworks/opt/telephony/src/java/com/android/internal/telephony/NitzStateMachine.java
389    private void handleTimeZoneFromNitz(TimeStampedValue<NitzData> nitzSignal) {390            //一系列处理
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) {//setting中是否是自动获取时区
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    }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    }

可以看到最后时区信息被设置到了alarmManager中发送的广播是android.intent.action.NETWORK_SET_TIMEZONE

3.2 时间信息的处理 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. 计算从halservice接收到运营商的消息到现在系统经过的时间
473                long millisSinceNitzReceived = elapsedRealtime - nitzSignal.mElapsedRealtime;
474                if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) {475                    //边界处理
480                    return;
481                }
482
483                // Adjust the NITZ time by the delay since it was received to get the time now.//2.计算当前的绝对时间
484                long adjustedCurrentTimeMillis =
485                        nitzSignal.mValue.getCurrentTimeInMillis() + millisSinceNitzReceived;//3.计算网络时间和系统时间的差值
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    }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    }

可以看到最终是将时间设置到了SystemClock中,最终其实也是设置给了AlarmManagerService中。

4.Setting中开启自动更新时间和时区的流程

Setting中开关会改变Setting数据库中Settings.Global.AUTO_TIME_ZONE和Settings.Global.AUTO_TIME_ZONE的值,而在第二阶段中创建处理handler ServiceStateTracker时,会注册一个数据库监听,如下:

/frameworks/opt/telephony/src/java/com/android/internal/telephony/ServiceStateTracker.java
487    public ServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) {//makeNitzStateMachine会新建一个NitzStateMachine 接着初始化TimeServiceHelper并调用setListener注册数据库监听 第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.//开启一个线程
519        mHandlerThread = new HandlerThread(LocaleTracker.class.getSimpleName());
520        mHandlerThread.start();...//绑定处理时间和时区信息的处理函数
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;//创建TimeServiceHelper并设置监听
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;//注册数据库监听
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    }

从代码可以看到,当数据库里的Settings.Global.AUTO_TIME和Settings.Global.AUTO_TIME_ZONE发生变化时,会调用到onTimeDetectionChange和onTimeZoneDetectionChange,接着就会回调到handleAutoTimeEnabled和handleAutoTimeZoneEnabled,分别处理自动获取时间和自动获取时区,

/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    }

可以看到最终仍然是调用setAndBroadcastNetworkSetTime和setAndBroadcastNetworkSetTimeZone去更新时间,但是只有在mSavedNitzTime和mSavedTimeZoneId被设置过的情况下才能进行更新。那么mSavedNitzTime和mSavedTimeZoneId是在那里设置的呢?全局搜索一下这两个变量可以发现是在如下地方设置的:是在NITZ更新时间的时候会进行设置

/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时间更新机制

流程图:

1.NetworkTimeUpdateService的启动流程

NTP时间的更新是在NetworkTimeUpdateService进行初始化并设置更新的,NetworkTimeUpdateService是在SystemServer中启动的,如下:

/frameworks/base/services/java/com/android/server/SystemServer.java
724    private void startOtherServices() {...
736        NetworkTimeUpdateService networkTimeUpdater = null;...//isWatch是手表类设备为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.1NetworkTimeUpdateService的初始化:

/frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
101    public NetworkTimeUpdateService(Context context) {102        mContext = context;//初始化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        //一些关于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            //defaultServer 为time.android.com
64            final String defaultServer = res.getString(
65                    com.android.internal.R.string.config_ntpServer);//defaultTimeout 为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在初始化时获得了NtpTrustedTime的单例,NtpTrustedTime就是NTP的实现类。

1.2NetworkTimeUpdateService的启动:

/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.注册一些广播的接受者比如"android.intent.action.NETWORK_SET_TIME"和"com.android.server.NetworkTimeUpdateService.action.POLL"
125        registerForTelephonyIntents();
126        registerForAlarms();
127        //2.启动工作线程并设置handler
128        HandlerThread thread = new HandlerThread(TAG);
129        thread.start();
130        mHandler = new MyHandler(thread.getLooper());//3.注册监听网络状态变化的回调函数
131        mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
132        mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
133        //4.注册监听Setting数据库变化的函数
134        mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
135        mSettingsObserver.observe(mContext);
136    }

如果NITZ更新时间成功,并发送了ACTION_NETWORK_SET_TIME,那么NetworkTimeUpdateService的mNitzReceiver将会接收到这条广播并设置mNitzTimeSetTime,如下:

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    };

mNitzTimeSetTime如果没有设置的话初始值为NOT_SET = -1;

2.NTP时间更新

NTP时间更新触发有三种

2.1 在Settings中开启自动获取时间

NITZ没有生效时,如果连接了互联网并开启了自动设置时间那么会走如下流程

在Setting中开启自动获取时间,会触发数据库监听的回调函数onChange:

/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    }

之前在注册时传入的msg为EVENT_AUTO_TIME_CHANGED,handler是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 是否连接上网络,如果没有连接上网络,那么直接返回
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指的是NtpTrustedTime getCacheAge返回的是上次保存NTP的时间到当前时间的差值,如果还未获取到NTP时间那么返回Integer的最大值
168        if (mTime.getCacheAge() >= mPollingIntervalMs) {169            if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");//更新时间
170            mTime.forceRefresh();
171        }
172
173        if (mTime.getCacheAge() < mPollingIntervalMs) {174            // Obtained fresh fix; schedule next normal update//重置更新的定时器
175            resetAlarm(mPollingIntervalMs);//isAutomaticTimeRequested返回的是Setting数据库是否开启自动更新时间 这里返回true
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) {//如果不是自动更新时间被开启触发的时间更新才会走到下面//getNitzAge返回的是 SystemClock.elapsedRealtime() - mNitzTimeSetTime的值,如果NITZ无效,那么返回最大值//如果NITZ 刚刚才更新过,那么NTP不需要更新直接返回
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        //最终设置到系统中
220        SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
221    }

所以NTP时间函数的更新核心是 mTime.forceRefresh()

/frameworks/base/core/java/android/util/NtpTrustedTime.java
95    public boolean forceRefresh(Network network) {//这里的Server是Setting数据库中的Settings.Global.NTP_SERVER或者time.android.com
96        if (TextUtils.isEmpty(mServer)) {97            // missing server, so no trusted time available
98            return false;
99        }
100        //获取网络服务
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         //获取已连接的网络
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();//调用SntpClient的requestTime通过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 网络连接时

如果是自动获取时间已经开启,当网络连接时,会回调到NetworkTimeUpdateCallback的onAvailable,可以看到也是走的onPollNetworkTime只不过传入的Event是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) {//如果网络断开,那么mDefaultNetwork置为null
286            if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
287        }
288    }

2.3 到需要更新的时间时:

NetworkTimeUpdateService在系统启动时,注册了一个广播接收者当接收到ACTION_POLL时,会回调到 如下函数:

/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) {//其中mHandler是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    }

可以看到最终也走到了onPollNetworkTime;剩下的都是一样的。当设备开机后第一次通过NTP更新时间后会调用resetAlarm来重置定时器;

这时就会触发一个定时器,到时间后发送广播

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 与 NTP 小结

现在Android通过网络同步时间有两种方式:NITZ和NTP,它们使用的条件不同,可以获取的信息也不一样;勾选自动同步功能后,手机首先会尝试NITZ方式,若获取时间失败,则使用NTP方式
1.NITZ(network identity and time zone)同步时间

NITZ是一种GSM/WCDMA基地台方式,必须插入SIM卡,且需要operator支持;可以提供时间和时区信息

中国大陆运营商基本是不支持的

2.NTP(network time protocol)同步时间

NTP在无SIM卡或operator不支持NITZ时使用,单纯通过网络(GPRS/WIFI)获取时间,只提供时间信息,没有时区信息(因此在不支持NITZ的地区,自动获取时区功能实际上是无效的)

NTP还有一种缓存机制:当前成功获取的时间会保存下来,当用户下次开启自动更新时间功能时会结合手机clock来进行时间更新。这也是没有任何网络时手机却能自动更新时间的原因。

Android9.0 本地时区和本地时间的自动更新机制相关推荐

  1. windows11打开隐藏的gpedit.msc本地组策略编辑器以及禁止自动更新系统

    目录 打开隐藏的gpedit.msc本地组策略编辑器 windows11禁止自动更新系统 打开隐藏的gpedit.msc本地组策略编辑器 1.新建.txt文本文件,输入如下代码 @echo off p ...

  2. 计算机无法自动更新,电脑时间不能自动更新怎么办?

    我们经常会遇到电脑时间不能自动更新,电脑时间不准确的现象,这个问题,可能对于很多电脑新手是个不小的麻烦,下面我就对此类问题分析,总结出几种原因,并给出电脑时间不能自动更新问题的解决办法,帮助大家解除此 ...

  3. html时间框自动更新,原生javascript实现自动更新的时间日期

    能够动态变化的事物总比静态的更能够吸引人,甚至更有实用效果,比如能够自动变化的时间日期效果就是如此,下面就通过代码实例介绍一下如何实现此效果,代码实例如下: 一.具体代码 脚本之家 var t = n ...

  4. 【V2.0】基于运动步数API的自动更新系统(支持断线重连、数据补偿)

    [V2.0]基于运动步数API的自动更新系统(支持断线重连.数据补偿) 前文: https://blog.csdn.net/weixin_53403301/article/details/122882 ...

  5. mysql 数据表 时间自动_MySQL数据库时间设置自动添加时间和自动更新时间

    MySQL字段中设置时间字段自动添加创建时间和自动更新时间设置, 设置字段类型为:timestamp 默认值设置为current_timestamp(), 更新时间字段字段类型为:timestamp ...

  6. Android 系统时间自动更新机制

    两种时间更新机制 NITZ NITZ(Network Identity and Time Zone,网络标识和时区),是一种用于自动配置本地的时间和日期的机制,同时也通过无线网向移动设备提供运营商信息 ...

  7. Android 系统时间自动更新机制--解决 “时间和日期不准确“

    两种时间更新机制 NITZ NITZ(Network Identity and Time Zone,网络标识和时区),是一种用于自动配置本地的时间和日期的机制,同时也通过无线网向移动设备提供运营商信息 ...

  8. sqlalchemy如何实现时间列自动更新?

    目标:数据表的时间列在其他列内容更新的时候,自动更新时间列到更新的时间 方法:数据库表模型如下:server_default表示初始时间,onupdate表示更新的时间 class MonitorDa ...

  9. 计算机右下角日期不能调整,电脑右下角时间不更新_电脑时间不能自动更新

    2016-12-10 14:32:21 你好,据我所知,WIN7系统设置电脑时间无法显示秒数.即使在时间设置格式中加入了秒SS,电脑右下角托盘中还是只显示到分.且没有必要显示到秒,若要暂时显示到秒,可 ...

最新文章

  1. mysql技术内幕innodb存储引擎——表索引算法和锁_(转)Mysql技术内幕InnoDB存储引擎-表索引算法和锁...
  2. 词云制作 Python
  3. C#装箱和拆箱(值类型和引用类型之间的转换)
  4. Web服务初探:用Demo学Web服务系列(7)——XML的相关知识
  5. 配置静态路由进阶实验
  6. Spring Security 3.1 自定义 authentication provider
  7. RTX5 | STM32H743+CubeMX+RTX5+两路FDCAN驱动+CANopen协议
  8. Brush、Color、String相互转换
  9. 科技圈晒开工福利!小米最直接,腾讯最传统,阿里最豪气,你们家的呢?
  10. VirualBox安装XP_64bit+中文语言包
  11. java lambda函数_Java中的Lambda函数
  12. Win10下Matlab r2018a 64位 中文破解版的安装以及破解方法
  13. unimodal_palindromic——回文串dp动规
  14. 风险管理可分为哪两类?具体方法是什么?
  15. 标签体系下的用户画像建设小指南
  16. python机器学习之SVM分类预测电芯状态
  17. html 字符画,字符画
  18. python选股策略,金叉,死叉,绿色云
  19. 【送两本】计算机领域神书《深入理解计算机系统》
  20. 【学习日志】2022.08.26 C#单例模式 Tostring Utils

热门文章

  1. Java 动态生成推广海报,带用户头像、昵称、二维码
  2. 如何用空气质量查询API接口进行快速开发
  3. 2020.10.7--PS--填充图层、调整图层、调整图层与剪贴蒙版
  4. BZOJ 4605 崂山白花蛇草水 权值线段树+K-D树
  5. symbian学习转载
  6. 今日头条引流脚本,微商引流工具
  7. ilitek win10 触摸屏驱动_德国布拉本达(Brabender)触摸屏维修常见故障_触摸屏维修吧...
  8. 前端---HTML QQ空间主页制作
  9. win2008sever CA证书颁发服务器部署
  10. 新手小白设计干货|使用ps制作一张简单海报