原址

NTP和NITZ的简介

NITZ:Network Identity and Time Zone(网络标识和时区),NITZ是一种GSM/WCDMA基地台方式,必须插入SIM卡,且需要运营商支持,从运营商处获取时间和时区信息。中国大陆运营商基本是不支持的。 
NTP:Network Time Protocol(网络时间协议),用来同步网络中各个计算机的时间的协议。在Android设备中,NTP更新时间往往是通过网络(GPRS或WIFI)向NTP服务器获取时间(不包含时区信息)。

NITZ更新时间流程

NITZ更新时间依赖运营商,当运营商基站发出更新时间的消息,基站附近的手机接收到对应消息后,会通过RIL层上报UNSOL_NITZ_TIME_RECEIVED事件,此时ServiceStateTracker便会处理相关时间更新流程,相关时序图如下: 

由于NITZ主要依赖于运营商,但在国内移动和联通貌似不怎么好用,在这里就不在详细说了,简单总结下如下: 
1、在ServiceStateTracker构造方法里调用setOnNITZTime注册RIL事件RIL_UNSOL_NITZ_TIME_RECEIVED 
2、RIL层上报RIL_UNSOL_NITZ_TIME_RECEIVED,在ServiceStateTracker的handleMessage里处理 
3、调用ServiceStateTracker的setTimeFromNITZString设置时间和时区,在setAndBroadcastNetworkSetTime里调用setCurrentTimeMillis设置系统时间,并发送广播通知NetworkTimeUpdateService

NTP时间更新流程

NTP时间更新是通过网络(即GPRS或WIFI)去获取时间,在NetworkTimeUpdateService实现的,整体流程如下: 

在SystemServer中, 会启动NetworkTimeUpdateService服务:

//frameworks/base/services/java/com/android/server/SystemServer.javaprivate void startOtherServices() {try {// ......if (!disableNetwork && !disableNetworkTime) {try {Slog.i(TAG, "NetworkTimeUpdateService");networkTimeUpdater = new NetworkTimeUpdateService(context);} catch (Throwable e) {reportWtf("starting NetworkTimeUpdate service", e);}}// ......}       // ......mActivityManagerService.systemReady(new Runnable() {@Overridepublic void run() {// ......try {if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();} catch (Throwable e) {reportWtf("Notifying NetworkTimeService running", e);}// ......}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

即SystemServer中会调用networkTimeUpdaterF.systemRunning()注册各种监听, 如下:

//frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.javapublic void systemRunning() {registerForTelephonyIntents();registerForAlarms();registerForConnectivityIntents();HandlerThread thread = new HandlerThread(TAG);thread.start();mHandler = new MyHandler(thread.getLooper());// Check the network time on the new threadmHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);mSettingsObserver.observe(mContext);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在registerForTelephonyIntents中监听如下动作: 
- ACTION_NETWORK_SET_TIME 
- ACTION_NETWORK_SET_TIMEZONE

在registerForAlarms中监听什么 ? 
在registerForConnectivityIntents中监听网络状态改变(CONNECTIVITY_ACTION)的广播. 
在SettingsObserver里监听Settings.Global.AUTO_TIME值的改变, 在这个值发生变化时, 发EVENT_AUTO_TIME_CHANGE消息

在监听到上述动作/广播后, 会在MyHandler中得到处理, 即调用onPollNetworkTime(), 如下:

    private void onPollNetworkTime(int event) {// If Automatic time is not set, don't bother.if (!isAutomaticTimeRequested()) return;final long refTime = SystemClock.elapsedRealtime();// 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 currentTime = System.currentTimeMillis();if (DBG) Log.d(TAG, "System time = " + currentTime);// Get the NTP timeif (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs|| event == EVENT_AUTO_TIME_CHANGED) {if (DBG) Log.d(TAG, "Before Ntp fetch");// force refresh NTP cache when outdatedif (mTime.getCacheAge() >= mPollingIntervalMs) {/* M: For multiple NTP server retry */// mTime.forceRefresh();int index = mTryAgainCounter % mNtpServers.size();if (DBG) Log.d(TAG, "mTryAgainCounter = " + mTryAgainCounter + ";mNtpServers.size() = " + mNtpServers.size() + ";index = " + index+ ";mNtpServers = " + mNtpServers.get(index));if (mTime instanceof NtpTrustedTime) {((NtpTrustedTime)mTime).setServer(mNtpServers.get(index));mTime.forceRefresh();((NtpTrustedTime)mTime).setServer(mDefaultServer);} else {mTime.forceRefresh();}/* M: For multiple NTP server retry */}// only update when NTP time is freshif (mTime.getCacheAge() < mPollingIntervalMs) {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 timeif (DBG && mLastNtpFetchTime == NOT_SET&& Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {Log.d(TAG, "For initial setup, rtc = " + currentTime);}if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);// Make sure we don't overflow, since it's going to be converted to an intif (ntp / 1000 < Integer.MAX_VALUE) {SystemClock.setCurrentTimeMillis(ntp);}} else {if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);}mLastNtpFetchTime = SystemClock.elapsedRealtime();} else {// Try again shortlymTryAgainCounter++;if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {resetAlarm(mPollingIntervalShorterMs);} else {// Try much latermTryAgainCounter = 0;resetAlarm(mPollingIntervalMs);}return;}}resetAlarm(mPollingIntervalMs);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

没有太复杂的逻辑, 就是网络获取时间, 并与本机时间比较, 如果时间差大于mTimeErrorThresholdMs值, 就更新本机时间.

这里涉及到几个阈值: 
mPollingIntervalMs: 当NTP时间获取成功后,再次请求NTP时间的间隔 
mPollingIntervalShorterMs: 当NTP时间获取失败后,再次请求NTP时间的间隔 
mTimeErrorThresholdMs: 当NTP时间和系统时间不相同时,如果时间差大于此阀值,则更新系统时间 
mTryAgainCounter: Retry的最大次数

这几个值都是在系统资源的config.xml配置的:

//frameworks/base/core/res/res/values/config.xml<!-- Remote server that can provide NTP responses. --><string translatable="false" name="config_ntpServer">2.android.pool.ntp.org</string><!-- Normal polling frequency in milliseconds --><integer name="config_ntpPollingInterval">864000000</integer><!-- Try-again polling interval in milliseconds, in case the network request failed --><integer name="config_ntpPollingIntervalShorter">60000</integer><!-- Number of times to try again with the shorter interval, before backingoff until the normal polling interval. A value < 0 indicates infinite. --><integer name="config_ntpRetry">3</integer><!-- If the time difference is greater than this threshold in milliseconds,then update the time. --><integer name="config_ntpThreshold">5000</integer><!-- Timeout to wait for NTP server response. --><integer name="config_ntpTimeout">20000</integer>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

更新时间的关键是调用 mTime.forceRefresh(), 即NtpTrustedTime类的forceRefresh()方法, 如下:

//frameworks/base/core/java/android/util/NtpTrustedTime.javapublic boolean forceRefresh() {// ......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;}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

即调用SntpClient, 通过UDP方式向NTP Server获取时间:

//frameworks/base/core/java/android/net/SntpClient.javapublic boolean requestTime(String host, int timeout) {DatagramSocket socket = null;try {socket = new DatagramSocket();socket.setSoTimeout(timeout);InetAddress address = InetAddress.getByName(host);byte[] buffer = new byte[NTP_PACKET_SIZE];DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT);// set mode = 3 (client) and version = 3// mode is in low 3 bits of first byte// version is in bits 3-5 of first bytebuffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);// get current time and write it to the request packetlong requestTime = System.currentTimeMillis();long requestTicks = SystemClock.elapsedRealtime();writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);socket.send(request);// read the responseDatagramPacket response = new DatagramPacket(buffer, buffer.length);socket.receive(response);long responseTicks = SystemClock.elapsedRealtime();long responseTime = requestTime + (responseTicks - requestTicks);// extract the resultslong originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime);long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2;// save our results - use the times on this side of the network latency// (response rather than request time)mNtpTime = responseTime + clockOffset;mNtpTimeReference = responseTicks;mRoundTripTime = roundTripTime;} catch (Exception e) {if (false) Log.d(TAG, "request time failed: " + e);return false;} finally {if (socket != null) {socket.close();}}return true;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

上面requestTime()代码涉及到NTP取时的校正问题, 请参考NTP协议与计算

如果遇到NTP无法更新的问题, 需要分析logcat, 看看是否有SocketException或unknown host之类的网络错误. 可能的原因: 
- 网络不可用 
- NTP Server连接不上

总结

  1. NITZ的优先级要高于NTP的优先级,当NITZ更新系统时间后,NTP即使触发更新条件,也会检查NITZ更新时间距今是否超过864000000毫秒(10天,config_ntpPollingInterval),若不满10天,则重设Alarm并取消此次NTP更新请求。
  2. NITZ主要依赖于运营商上报,NTP则主要依赖于网络环境,NITZ通过被动接收获取时间,NTP通过访问NTP Server主动获取网络时间,最后都是通过调用SystemClock.setCurrentTimeMillis更新本机时间。

参考: 
- Android中时间维护 
- Android 7.1.1时间更新NITZ和NTP详解 
- Android中通过NTP服务器获取时间功能源码分析

Android5.x(NTP和NITZ)时间同步相关推荐

  1. 嵌入式的时间概念:GMT,UTC,CST,DST,RTC,NTP,SNTP,NITZ

    GMT,UTC,CST,DST,RTC,NTP,SNTP,NITZ: 嵌入式的时间 ref 嵌入式开发中,几个时间概念? 彻底搞懂UTC时间 NTP协议详解 使用NTP协议获取网络时间戳(C/C++实 ...

  2. linux设置ntp开机同步时间同步,linux ntp时间同步

    一.搭建时间同步服务器 1.编译安装ntp server rpm -qa | grep ntp 若没有找到,则说明没有安装ntp包,从光盘上找到ntp包,使用 rpm -Uvh ntp***.rpm ...

  3. 配置NTP服务器进行时间同步

    配置NTP服务器进行时间同步 在集群部署服务的时候如果集群的时间各部相同会导致业务上的问题,所以可以找一个机器,作为时间服务器,所有的机器与这台集群时间进行定时的同步 时间服务器搭建ntp服务 检测n ...

  4. ntp服务及时间同步问题

    https://www.cnblogs.com/yjbjingcha/p/6918917.html ntp服务及时间同步问题 今有一小型项目,全然自主弄,原来以为非常easy的NTP服务.我给折腾了2 ...

  5. Windows下安装NTP服务器——搭建时间同步服务器

    Windows下安装NTP服务器--搭建时间同步服务器 NTP服务器介绍 NTP服务器[Network Time Protocol(NTP)]是用来使计算机时间同步化的一种协议,它可以使计算机对其服务 ...

  6. ntp时间服务器——时间同步

    具体两种模式: 1.服务器数量比较少,可以直接与时间服务器同步 2.本地服务器较多,在本地自建时间同步服务器 时间同步的两个命令: ntpd : 校准时间,一点点的校准过来时间的,最终把时间慢慢的校正 ...

  7. ntp时间服务器 时间同步

    ntp时间服务器 时间同步 2017年10月15日 11:05:30 centos2015 阅读数:1996 标签: ntpdtimentpdate 更多 个人分类: Linux 版权声明:本文为博主 ...

  8. 如何用ntp实现服务器时间同步!!!

    如何用ntp实现服务器时间同步!!! 什么是NTP 一.ntp服务器时间同步 1.获取阿里云服务器时间同步到服务器 2.同步服务器 什么是NTP NTP是用来使计算机时间同步化的一种协议,全称是Net ...

  9. linux系统上安装ntp服务,linux时间同步ntp服务的安装与配置

    NTP是网络时间协议(Network Time Protocol),它是用来同步网络中各个计算机的时间的协议. 1.安装ntp服务,要使用时间同步.那么服务端与客户端都需要使用如下命令安装NTP软件包 ...

最新文章

  1. java实体属性对应mysql和SQL Server 和Oracle 数据类型对应
  2. php 接口curl,php中接口强大之处php_curl
  3. [转载]ASP.NET Core文件上传与下载(多种上传方式)
  4. Scala的package用法
  5. SAP Spartacus logout的拦截
  6. 如何重做计算机系统软件,电脑卡如何一键重做Win7旗舰版
  7. 《少年的你》惊现魅族手机,“心酸”回应:纯属意外 小破厂没钱植入
  8. 【java】修改包访问的数据
  9. 自动化部署mysql主从复制集群_使用docker部署mysql主从复制集群
  10. 我的世界服务器显示unknown,我的世界找不到家怎么办-​我的世界unknown
  11. 程序员在网吧办公是什么感觉?网友:在被打的边缘疯狂试探!
  12. centos下安装php-fpm,centos下怎么安装php-fpm
  13. 2021年电工(初级)考试试卷及电工(初级)考试平台
  14. 开机后显示服务器正在启动,电脑开机后卡在Windows正在启动界面上怎么办?
  15. 计算机专业有哪些【含金量超高竞赛】?
  16. 听说一个漂亮的小姐姐图片是程序员无法抗拒的
  17. 关于关于高博3d2d程序报错的改动
  18. 计算机毕业设计SSM大学生学习交友平台【附源码数据库】
  19. 微信支付金额浮点分计算php,复盘微信支付金额不正确问题解决过程——PHP浮点型计算...
  20. win10打开excel和word报错0xc000012f

热门文章

  1. python hello world
  2. 029 RDD Join相关API,以及程序
  3. O365(世纪互联)SharePoint 之使用Designer报错
  4. JS 浏览器差异(IE和FF)
  5. Node.js 提升运行效率
  6. STM32F429HAL库时钟系统学习笔记
  7. rost反剽窃检测系统_色情、低俗信息没得治?今日头条这款检测工具,240万人都在用!...
  8. 交流电机数字控制系统_干货 | 简述伺服电机和步进电机的六大性能差异
  9. 19天津大学计算机考研群,19天津大学金融专硕431经验贴
  10. think php5关联模型,thinkphp5中关联模型的定义与使用方法