Android 系统时间自动更新机制

Posted Jason_Wang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 系统时间自动更新机制相关的知识,希望对你有一定的参考价值。

两种时间更新机制

NITZ

NITZ(Network Identity and Time Zone,网络标识和时区),是一种用于自动配置本地的时间和日期的机制,同时也通过无线网向移动设备提供运营商信息。NITZ是自从PHASE 2+ RELEASE 96 的GSM中的可选功能,经常被用来自动更新移动电话的系统时钟。NITZ需要运营商网络支持(通过CS网络),目前国内电信、移动都支持NITZ方式更新时间日期,而联通目前不支持。

参考: https://en.wikipedia.org/wiki/NITZ

NTP

NTP:NTP(Network Time Protocol)提供准确时间,首先要有准确的时间来源,这一时间应该是国际标准时间UTC。 NTP获得UTC的时间来源可以是原子钟、天文台、卫星,也可以从Internet上获取。这样就有了准确而可靠的时间源。时间按NTP服务器的等级传播。与NITZ不同的是,NTP需要从专门的NTP服务器来获取时间,只要手机连接上网络,都可以实现时间的更新。

参考: https://en.wikipedia.org/wiki/Network_Time_Protocol

Android如何更新系统时间

android有一个专门的系统服务 NetworkTimeUpdateServcie来负责更新系统时间,该服务在系统启动时在SystemServer.java中被创建:

    if (!disableNetwork && !disableNetworkTime) 
        try 
            Slog.i(TAG, "NetworkTimeUpdateService");
            networkTimeUpdater = new NetworkTimeUpdateService(context);
        catch (Throwable e) 
            reportWtf("starting NetworkTimeUpdate service", e);
        
    
    ...
    ...
    try 
        if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
     catch (Throwable e) 
        reportWtf("Notifying NetworkTimeService running", e);
    

源码: /frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java

服务初始化

NetworkTimeUpdateService初始时会:

  • 注册RIL的ACTION_NETWORK_SET_TIME以及ACTION_NETWORK_SET_TIMEZONE事件,以接受来自Telephony FW的NITZ时间更新;
  • 监听 ACTION_POLL事件(定时更新时间)以及手机网络连接状态;
  • 发送消息同步NTP时间
  • 监听 Settings中“自动更新时间”选项的变化
    /** Initialize the receivers and initiate the first NTP request */
    public void systemRunning() 
        registerForTelephonyIntents();
        registerForAlarms();
        registerForConnectivityIntents();

        HandlerThread thread = new HandlerThread(TAG);
        thread.start();
        mHandler = new MyHandler(thread.getLooper());
        // Check the network time on the new thread
        mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();

        mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
        mSettingsObserver.observe(mContext);
    

同步NTP时间

如果没有收到NITZ时间的更新并且NTP超过一定间隔没有更新时间,服务会主动去同步NTP时间:

    // force refresh NTP cache when outdated
    if (mTime.getCacheAge() >= mPollingIntervalMs) 
        mTime.forceRefresh();
    

NTP从服务器获取时间:

    @Override
    public boolean forceRefresh() 
    ...
        // We can't do this at initialization time: ConnectivityService might not be running yet.
        synchronized (this) 
            if (mCM == null) 
                mCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
            
        
    ...
        if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
        final SntpClient client = new SntpClient();
        if (client.requestTime(mServer, (int) mTimeout)) 
            mHasCache = true;
            mCachedNtpTime = client.getNtpTime();
            mCachedNtpElapsedRealtime = client.getNtpTimeReference();
            mCachedNtpCertainty = client.getRoundTripTime() / 2;
           return true;
         else 
           return false;
        
    

源码: /frameworks/base/core/java/android/util/NtpTrustedTime.java

接收NITZ时间

Telephony Framework层在接收到最新的NITZ时间后,会主动发送广播请求更新系统时间,NetworkTimeUpateService接收到广播后,保存相应的NITZ时间,下一次poll请求时,就会将该事件更新为系统时间。

    private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() 
        @Override
        public void onReceive(Context context, Intent intent) 
            String action = intent.getAction();
            if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) 
                mNitzTimeSetTime = SystemClock.elapsedRealtime();
             else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) 
                mNitzZoneSetTime = SystemClock.elapsedRealtime();
            
        
    ;

监听Settings中 “自动确定时间和日期”的变化

在setting中勾选“自动确定时间和日期”、“自动确定时区”后对key值为AUTO_TIME和AUTO_TIME_ZONE的Preference进行了赋值.

源码路径:packages/apps/Settings/src/com/android/settings/DateTimeSettings.java

    void observe(Context context) 
            ContentResolver resolver = context.getContentResolver();
            resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
                    false, this);
        

        @Override
        public void onChange(boolean selfChange) 
            mHandler.obtainMessage(mMsg).sendToTarget();
        

NetworkTimeUpdateService在检测到key值改变的时,就会发送一个消息EVENT_AUTO_TIME_CHANGED;handler接到消息后进行消息处理调用onPollNetworkTime(msg.what):

    public void handleMessage(Message msg) 
        switch (msg.what) 
            case EVENT_AUTO_TIME_CHANGED:
            case EVENT_POLL_NETWORK_TIME:
            case EVENT_NETWORK_CONNECTED:
                onPollNetworkTime(msg.what);
                break;
        
    

在onPollNetworkTime方法中先判断是否勾选“自动更新时间”,如果没勾选直接退出,如果勾选了再看。如果NITZ已经更新了(不为NOT_SET(-1)),且更新间隔小于mPollingIntervalMs(mPollingIntervalMs=24*60*60*1000),则直接用NITZ更新系统时间,否则用NTP同步时间。

    // If NITZ time was received less than mPollingIntervalMs time ago,
    // no need to sync to NTP.
    if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) 
        resetAlarm(mPollingIntervalMs);
        return;
    

     final long ntp = mTime.currentTimeMillis();
     mTryAgainCounter = 0;
     // If the clock is more than N seconds off or this is the first time it's been
     // fetched since boot, set the current time.
     if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
                || mLastNtpFetchTime == NOT_SET) 
        // Set the system time
        ......
        if (ntp / 1000 < Integer.MAX_VALUE) 
                           SystemClock.setCurrentTimeMillis(ntp);
     

当从NTP服务器上获取的时间和当前时间之差的绝对值大于一个阀值,则认为当前时间错误,需要更新时间。

总结:

  • 时间自动同步选项未勾选,不主动更新时间,直接返回;
  • NITZ已同步且上次NITZ同步未超过24小时,则设置定时器24小时后再触发同步,即广播NetworkTimeUpdateService.ACTION_POLL;
  • NTP上次成功同步超过24小时或用户勾选自动同步选项,则进行下面的NTP同步,否则同上设置定时器24小时后再触发同步

参考文献

以上是关于Android 系统时间自动更新机制的主要内容,如果未能解决你的问题,请参考以下文章

UTC/GMT/CST/RTC

JDK的Atomic原子操作类实现机制

程序代码中的时区问题

gmt时间格式是啥意思

Android 时间同步机制及常见问题分析

at 8:00 PM PT (03:00 UTC)是啥时间?换算成北京时间是几点?