Android 5.1.1 时间同步方式
来源:互联网 发布:国家承认网络教育 编辑:程序博客网 时间:2024/06/06 14:20
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.java private 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() { @Override public void run() { // ...... try { if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning(); } catch (Throwable e) { reportWtf("Notifying NetworkTimeService running", e); } // ...... } }
即SystemServer中会调用networkTimeUpdaterF.systemRunning()注册各种监听, 如下:
//frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java 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); }
在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 time if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs || event == EVENT_AUTO_TIME_CHANGED) { if (DBG) Log.d(TAG, "Before Ntp fetch"); // force refresh NTP cache when outdated if (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 fresh if (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 time if (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 int if (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 shortly mTryAgainCounter++; if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) { resetAlarm(mPollingIntervalShorterMs); } else { // Try much later mTryAgainCounter = 0; resetAlarm(mPollingIntervalMs); } return; } } resetAlarm(mPollingIntervalMs); }
没有太复杂的逻辑, 就是网络获取时间, 并与本机时间比较, 如果时间差大于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 backing off 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>
更新时间的关键是调用 mTime.forceRefresh(), 即NtpTrustedTime类的forceRefresh()方法, 如下:
//frameworks/base/core/java/android/util/NtpTrustedTime.java public 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; } }
即调用SntpClient, 通过UDP方式向NTP Server获取时间:
//frameworks/base/core/java/android/net/SntpClient.java public 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 byte buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3); // get current time and write it to the request packet long requestTime = System.currentTimeMillis(); long requestTicks = SystemClock.elapsedRealtime(); writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime); socket.send(request); // read the response DatagramPacket response = new DatagramPacket(buffer, buffer.length); socket.receive(response); long responseTicks = SystemClock.elapsedRealtime(); long responseTime = requestTime + (responseTicks - requestTicks); // extract the results long 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; }
上面requestTime()代码涉及到NTP取时的校正问题, 请参考NTP协议与计算
如果遇到NTP无法更新的问题, 需要分析logcat, 看看是否有SocketException或unknown host之类的网络错误. 可能的原因:
- 网络不可用
- NTP Server连接不上
总结
- NITZ的优先级要高于NTP的优先级,当NITZ更新系统时间后,NTP即使触发更新条件,也会检查NITZ更新时间距今是否超过864000000毫秒(10天,config_ntpPollingInterval),若不满10天,则重设Alarm并取消此次NTP更新请求。
- NITZ主要依赖于运营商上报,NTP则主要依赖于网络环境,NITZ通过被动接收获取时间,NTP通过访问NTP Server主动获取网络时间,最后都是通过调用SystemClock.setCurrentTimeMillis更新本机时间。
参考:
- Android中时间维护
- Android 7.1.1时间更新NITZ和NTP详解
- Android中通过NTP服务器获取时间功能源码分析
- Android 5.1.1 时间同步方式
- android 时间同步
- Android SNTP 时间同步
- android时间自动同步
- Android网络时间同步
- android NTP时间同步
- Android 同步网络时间
- Android时间同步流程
- 几种时间同步的方式
- 常见的四种时间同步方式
- linux同步ntpd服务器时间方式
- linux同步ntpd服务器时间方式
- Linux上三种方式--ntp时间同步
- Android中的网络时间同步
- Android中的网络时间同步
- android手机时间自动同步
- Android中的网络时间同步
- Android中的网络时间同步 !!!!!!!!
- D3D11的简单字体
- 好文章保存书签
- JS 时间戳转换成几天前
- Adnroid LearningNotes
- 关注程序性能(3) jvisualvm监视远程主机
- Android 5.1.1 时间同步方式
- CATIA DELMIA ENOVIA v5-6R2015 SP6 Update Only Win32_64 2CD
- Space Elevator POJ
- 【视频变化检测】2017CVPR Spatio-Temporal Self-Organizing Map Deep Network for Dynamic Object Detection from
- 用指向指针的指针的方法对n个整数进行排序。
- LightGBM原理简介
- JeeSite相关技术摘点(六)
- C#个人重构之登录
- 华硕FX53VD6700笔记本如何u盘装系统win7教程