Android 7.1.1时间更新NITZ和NTP详解

1、NTP和NITZ简介

最近在项目中遇到手机首次插上移动卡时不能自动更新时间的问题,就特意跟了下Android系统中手机时间更新有两种方式NTP和NITZ,下面先来看看NTP和NITZ的简介
NITZ:Network Identity and Time Zone(网络标识和时区),是一种用于自动配置本地的时间和日期的机制,需要运营商支持,可从运营商获取时间和时区具体信息。
NTP:Network Time Protocol(网络时间协议),用来同步网络中各个计算机的时间的协议。在手机中,NTP更新时间的方式是通过GPRS或wifi向特定服务器获取时间信息(不包含时区信息)。
接着我们来看看两种方式具体更新流程

2、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

3、NTP时间更新流程

NTP时间更新主要依赖于GPRS和wifi,即通过网络的方式去获取时间,在NetworkTimeUpdateService中调用onPollNetworkTime访问NtpServer获取网络时间,我们先来看看整体流程

当SystemServer启动,会调用networkTimeUpdaterF.systemRunning()初始化各种NTP request监听

  1. public void systemRunning() {

  2. registerForTelephonyIntents();

  3. registerForAlarms();

  4. registerForConnectivityIntents();

  5. HandlerThread thread = new HandlerThread(TAG);

  6. thread.start();

  7. mHandler = new MyHandler(thread.getLooper());

  8. // Check the network time on the new thread

  9. mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();

  10. mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);

  11. mSettingsObserver.observe(mContext);

  12. }

在registerForTelephonyIntents中主要是监听ACTION_NETWORK_SET_TIME和ACTION_NETWORK_SET_TIMEZONE的广播,registerForAlarms中监听"com.android.server.NetworkTimeUpdateService.action.POLL"广播,registerForConnectivityIntents监听网络状态改变的广播,SettingsObserver里监听Settings.Global.AUTO_TIME值的改变

  1. //frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java

  2. private class MyHandler extends Handler {

  3. public MyHandler(Looper l) {

  4. super(l);

  5. }

  6. @Override

  7. public void handleMessage(Message msg) {

  8. switch (msg.what) {

  9. case EVENT_AUTO_TIME_CHANGED:

  10. case EVENT_POLL_NETWORK_TIME:

  11. case EVENT_NETWORK_CHANGED:

  12. onPollNetworkTime(msg.what);

  13. break;

  14. }

  15. }

  16. }

在这个MyHandler中可以看到,当上面某种监听触发时都会调用onPollNetworkTime,而这个方法里主要调用了onPollNetworkTimeUnderWakeLock,接下来看看这个方法

  1. //frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java

  2. private void onPollNetworkTimeUnderWakeLock(int event) {

  3. final long refTime = SystemClock.elapsedRealtime();

  4. // If NITZ time was received less than mPollingIntervalMs time ago,

  5. // no need to sync to NTP.

  6. if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {

  7. resetAlarm(mPollingIntervalMs);

  8. return;

  9. }

  10. final long currentTime = System.currentTimeMillis();

  11. if (DBG) Log.d(TAG, "System time = " + currentTime);

  12. // Get the NTP time

  13. if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs

  14. || event == EVENT_AUTO_TIME_CHANGED) {

  15. if (DBG) Log.d(TAG, "Before Ntp fetch");

  16. // force refresh NTP cache when outdated

  17. if (mTime.getCacheAge() >= mPollingIntervalMs) {

  18. mTime.forceRefresh();

  19. }

  20. // only update when NTP time is fresh

  21. if (mTime.getCacheAge() < mPollingIntervalMs) {

  22. final long ntp = mTime.currentTimeMillis();

  23. mTryAgainCounter = 0;

  24. // If the clock is more than N seconds off or this is the first time it's been

  25. // fetched since boot, set the current time.

  26. if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs

  27. || mLastNtpFetchTime == NOT_SET) {

  28. // Set the system time

  29. if (DBG && mLastNtpFetchTime == NOT_SET

  30. && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {

  31. Log.d(TAG, "For initial setup, rtc = " + currentTime);

  32. }

  33. if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);

  34. // Make sure we don't overflow, since it's going to be converted to an int

  35. if (ntp / 1000 < Integer.MAX_VALUE) {

  36. SystemClock.setCurrentTimeMillis(ntp);

  37. }

  38. } else {

  39. if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);

  40. }

  41. mLastNtpFetchTime = SystemClock.elapsedRealtime();

  42. } else {

  43. // Try again shortly

  44. mTryAgainCounter++;

  45. if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {

  46. resetAlarm(mPollingIntervalShorterMs);

  47. } else {

  48. // Try much later

  49. mTryAgainCounter = 0;

  50. resetAlarm(mPollingIntervalMs);

  51. }

  52. return;

  53. }

  54. }

  55. resetAlarm(mPollingIntervalMs);

  56. }

这个方法中主要调用了TrustedTime实例的forceRefresh方法去获取时间,获取之后通过mTime.currentTimeMillis获得获取成功之后的时间ntp,最后调用 SystemClock.setCurrentTimeMillis(ntp)设置系统时间有几个参数需要特别说下

  1. public NetworkTimeUpdateService(Context context) {

  2. mContext = context;

  3. mTime = NtpTrustedTime.getInstance(context);

  4. ...

  5. //正常的轮询频率

  6. mPollingIntervalMs = mContext.getResources().getInteger(

  7. com.android.internal.R.integer.config_ntpPollingInterval);

  8. //重试轮询间隔,以防网络请求失败

  9. mPollingIntervalShorterMs = mContext.getResources().getInteger(

  10. com.android.internal.R.integer.config_ntpPollingIntervalShorter);

  11. //再次尝试次数

  12. mTryAgainTimesMax = mContext.getResources().getInteger(

  13. com.android.internal.R.integer.config_ntpRetry);

  14. //如果时间差大于此阈值,则更新时间。

  15. mTimeErrorThresholdMs = mContext.getResources().getInteger(

  16. com.android.internal.R.integer.config_ntpThreshold);

  17. ...

  18. }

对应参数的配置如下,可能存在overlay替换,此处是framework里默认配置的值

  1. //frameworks/base/core/res/res/values/config.xml

  2. <!-- Normal polling frequency in milliseconds -->

  3. <integer name="config_ntpPollingInterval">86400000</integer>

  4. <!-- Try-again polling interval in milliseconds, in case the network request failed -->

  5. <integer name="config_ntpPollingIntervalShorter">60000</integer>

  6. <!-- Number of times to try again with the shorter interval, before backing

  7. off until the normal polling interval. A value < 0 indicates infinite. -->

  8. <integer name="config_ntpRetry">3</integer>

  9. <!-- If the time difference is greater than this threshold in milliseconds,

  10. then update the time. -->

  11. <integer name="config_ntpThreshold">5000</integer>

上面讲到主要通过TrustedTime实例的forceRefresh获取时间,下面就来跟下这个方法

  1. //frameworks/base/core/java/android/util/NtpTrustedTime.java

  2. public boolean forceRefresh() {

  3. if (TextUtils.isEmpty(mServer)) {

  4. // missing server, so no trusted time available

  5. return false;

  6. }

  7. // We can't do this at initialization time: ConnectivityService might not be running yet.

  8. synchronized (this) {

  9. if (mCM == null) {

  10. mCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);

  11. }

  12. }

  13. final NetworkInfo ni = mCM == null ? null : mCM.getActiveNetworkInfo();

  14. if (ni == null || !ni.isConnected()) {

  15. if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");

  16. return false;

  17. }

  18. if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");

  19. final SntpClient client = new SntpClient();

  20. if (client.requestTime(mServer, (int) mTimeout)) {

  21. mHasCache = true;

  22. mCachedNtpTime = client.getNtpTime();

  23. mCachedNtpElapsedRealtime = client.getNtpTimeReference();

  24. mCachedNtpCertainty = client.getRoundTripTime() / 2;

  25. return true;

  26. } else {

  27. return false;

  28. }

  29. }

上面方法中主要是通过SntpClient的requestTime根据传入的mServer获取时间,mServer是在调用NtpTrustedTime的getInstance中初始化的,具体如下

  1. public static synchronized NtpTrustedTime getInstance(Context context) {

  2. if (sSingleton == null) {

  3. final Resources res = context.getResources();

  4. final ContentResolver resolver = context.getContentResolver();

  5. final String defaultServer = res.getString(

  6. com.android.internal.R.string.config_ntpServer);

  7. final long defaultTimeout = res.getInteger(

  8. com.android.internal.R.integer.config_ntpTimeout);

  9. final String secureServer = Settings.Global.getString(

  10. resolver, Settings.Global.NTP_SERVER);

  11. final long timeout = Settings.Global.getLong(

  12. resolver, Settings.Global.NTP_TIMEOUT, defaultTimeout);

  13. final String server = secureServer != null ? secureServer : defaultServer;

  14. sSingleton = new NtpTrustedTime(server, timeout);

  15. sContext = context;

  16. }

  17. return sSingleton;

  18. }

config_ntpServer和config_ntpTimeout也是在framework下res中配置的,Settings.Global.NTP_SERVER和Settings.Global.NTP_TIMEOUT是配置在SettingProvider中的,就不在具体说明了。从上面代码可看出server是由secureServer和defaultServer决定的

  1. //frameworks/base/core/res/res/values/config.xml

  2. <!-- Remote server that can provide NTP responses. -->

  3. <string translatable="false" name="config_ntpServer">2.android.pool.ntp.org</string>

  4. <!-- Timeout to wait for NTP server response in milliseconds. -->

  5. <integer name="config_ntpTimeout">5000</integer>

其中SntpClient主要是提供访问Ntp server的一个类,在requestTime中主要通过DatagramSocket访问传入的server,来获取时间,具体实现如下:

  1. //frameworks/base/core/java/android/net/SntpClient.java

  2. public boolean requestTime(InetAddress address, int port, int timeout) {

  3. DatagramSocket socket = null;

  4. try {

  5. socket = new DatagramSocket();

  6. socket.setSoTimeout(timeout);

  7. byte[] buffer = new byte[NTP_PACKET_SIZE];

  8. DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, port);

  9. // set mode = 3 (client) and version = 3

  10. // mode is in low 3 bits of first byte

  11. // version is in bits 3-5 of first byte

  12. buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);

  13. // get current time and write it to the request packet

  14. final long requestTime = System.currentTimeMillis();

  15. final long requestTicks = SystemClock.elapsedRealtime();

  16. writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);

  17. socket.send(request);

  18. // read the response

  19. DatagramPacket response = new DatagramPacket(buffer, buffer.length);

  20. socket.receive(response);

  21. final long responseTicks = SystemClock.elapsedRealtime();

  22. final long responseTime = requestTime + (responseTicks - requestTicks);

  23. // extract the results

  24. final byte leap = (byte) ((buffer[0] >> 6) & 0x3);

  25. final byte mode = (byte) (buffer[0] & 0x7);

  26. final int stratum = (int) (buffer[1] & 0xff);

  27. final long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);

  28. final long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);

  29. final long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);

  30. /* do sanity check according to RFC */

  31. // TODO: validate originateTime == requestTime.

  32. checkValidServerReply(leap, mode, stratum, transmitTime);

  33. long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime);

  34. // receiveTime = originateTime + transit + skew

  35. // responseTime = transmitTime + transit - skew

  36. // clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2

  37. // = ((originateTime + transit + skew - originateTime) +

  38. // (transmitTime - (transmitTime + transit - skew)))/2

  39. // = ((transit + skew) + (transmitTime - transmitTime - transit + skew))/2

  40. // = (transit + skew - transit + skew)/2

  41. // = (2 * skew)/2 = skew

  42. long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2;

  43. if (DBG) {

  44. Log.d(TAG, "round trip: " + roundTripTime + "ms, " +

  45. "clock offset: " + clockOffset + "ms");

  46. }

  47. // save our results - use the times on this side of the network latency

  48. // (response rather than request time)

  49. mNtpTime = responseTime + clockOffset;

  50. mNtpTimeReference = responseTicks;

  51. mRoundTripTime = roundTripTime;

  52. } catch (Exception e) {

  53. if (DBG) Log.d(TAG, "request time failed: " + e);

  54. return false;

  55. } finally {

  56. if (socket != null) {

  57. socket.close();

  58. }

  59. }

  60. return true;

  61. }

其实Ntp说的明白点,就是通过网络去获取时间然后更新系统时间,具体流程上面也简单说了下,当遇到手机不能更新时间时,先要看看网络是否可用,网络可用的情况下,就得看看对应的NtpServer是否能够访问。曾经就遇到过移动的数据业务不能访问NtpServer,导致手机不能更新时间。一般可以通过ntp等关键字在log中搜索,一般是SocketException或unknown host的错误。也可以直接adb shell命令进去手机,然后利用ping NtpServer 查看当前服务器是否可访问。在这里需要说的一点是,NtpServer一般都是对应的网址,访问网络时会根据当前的运营商网络,找到对应的IP地址,再去访问,有可能存在同一个NtpServer联通网络可以访问而移动网络不行的情况。

4、NITZ和NTP的总结

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

Android 系统(214)---Android 7.1.1时间更新NITZ和NTP详解相关推荐

  1. 【Android系统】Android开机时间分析

    参看博客: https://www.jianshu.com/p/30fdf86c3462?from=singlemessage                                      ...

  2. android log抓取方法,Android系统之Android抓取各种log的方法

    Android系统之Android抓取各种log的方法 2018年11月25日 | 萬仟网移动技术 | 我要评论 android之android抓取各种log的方法 1.logcat (四类log b ...

  3. Android系统架构-[Android取经之路]

    摘要:本节主要来讲解Android的系统架构 阅读本文大约需要花费10分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢! 欢 ...

  4. android 服务端技术,移动应用服务器端开发(基于JSP技术)-2017 Android系统构架 Android系统构架.docx...

    Android系统构架 PAGE 1 目 录 TOC \o "1-3" \h \z \u 一.Android系统构架 1 二.Linux内核层 2 三.系统运行库层 3 (一)系统 ...

  5. 【android系统】android系统升级流程分析(二)---update升级包分析

    接下来我们将通过几篇文章来分析update.zip包在具体Android系统升级的过程,来理解Android系统中Recovery模式服务的工作原理.今天让我先来分析下升级包update.zip. 一 ...

  6. 【android系统】android系统升级流程分析(一)---recovery模式中进行update包升级流程分析

    今天我们直接来看下android中具体的升级过程是如何的. 升级流程概述 升级的流程图: 升级流程分析 第一步:升级包获取 升级获取可以通过远程下载,也可直接拷贝到指定目录即可. 第二步:准备升级 然 ...

  7. 【Android游戏开发十二】(保存游戏数据 [上文])详解SharedPreference 与 FIleInputStream/FileOutputStream将数据存储到SD卡中!

     李华明Himi 原创,转载务必在明显处注明: 转载自 [黑米GameDev街区] 原文链接:  http://www.himigame.com/android-game/327.html 很多童鞋说 ...

  8. Android基础入门教程——8.3.1 三个绘图工具类详解

    Android基础入门教程--8.3.1 三个绘图工具类详解 标签(空格分隔): Android基础入门教程 本节引言: 上两小节我们学习了Drawable以及Bitmap,都是加载好图片的,而本节我 ...

  9. 【Android语音合成TTS】百度语音接入方法,和使用技巧详解

    请尊重他人的劳动成果,转载请注明出处:[Android语音合成TTS]百度语音接入方法,和使用技巧详解 Ps. 依托于百度开放云,百度语音为合作伙伴提供了业界领先.永久免费的语音技术服务,目前已上线的 ...

最新文章

  1. 你需要一个首席数据官吗?
  2. 批处理 操作mysql_超简单使用批处理(batch)操作数据库
  3. C++smallest circle 获取外接给定点集的最小圆的中心和半径算法(附完整源码)
  4. JVM监控工具介绍jstack, jconsole, jinfo, jmap, jdb, jsta (Linux 如何查看进程的各线程的CPU占用 )
  5. ASP.NET 2.0+Atlas编写鼠标拖放程序(2)
  6. dotnet core开发体验之开始MVC
  7. Jdk 和 jre 的 关系和区别
  8. 关于Java的File.separator
  9. NYOJ--91--阶乘之和
  10. 虚拟机的文件系统,是否需要碎片清理功能?
  11. Linux命令iconv
  12. 为人处世:处世22条忠告
  13. 图解设计模式(23种)
  14. 盘点年度最佳10大顶级绘图软件,满足你99%的图表需求,赶紧收藏
  15. MMC-HVDC仿真模型,pscad柔性直流输电仿真mmc仿真模型,双端mmc模型,MMC为21电平NLM和均压控制
  16. 英式英语和美式英语的差异1-用词
  17. GPT磁盘及ID号介绍
  18. 深度学习系列笔记之统计基础
  19. 服务器使用上突然卡了,导致服务器卡顿的原因有哪些,该怎么处理?
  20. 直播源代码图片验证码

热门文章

  1. TCP协议的部分解析(1)
  2. mpeg4视频中,I帧、p帧、B帧的判定
  3. Java面试之JVM参数调优
  4. mysql 报错从 新安装
  5. JZOJ5146:港湾
  6. Android Studio 复制粘贴图片到drawable文件夹没有效果 - 解决方法
  7. 第六章 Android应用的生命周期
  8. AndroidUI 控件命名格式
  9. 找出字符串数组中的等于某字符串的所有索引位置
  10. 美国回应朝鲜会谈提议:朝方须履行国际义务