当前位置:   article > 正文

android系统源码之时间同步ntp流程_android 手机配置ntp服务同步时间

android 手机配置ntp服务同步时间

目录

一、时间同步模块启动

二、同步时间的定时器设置(重试逻辑)

1、注册定时器广播接收器 

2、时间同步间隔的设置

3、时间同步时间间隔的逻辑

4、同步时间

三、时间同步请求

1、获取ntp时间,并且设置系统时间

2、根据拿到的ntp时间设置系统时间

3、Sntp获取ntp服务器的时间

四、更改时区

五、ntp原理


一、时间同步模块启动

源码文件:frameworks/base/services/java/com/android/server/SystemServer.java

  1. //这个是使能时间同步模块的配置,正常都是使能的,这句话也是使能的,一开始以为默认false,后来才知道,
  2. //如果没有获取到这个配置,才会是false
  3. boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime", false);
  4. //这儿是将时间同步作为一个系统服务,system_server是一个进程,包括多个系统服务
  5. if (!isWatch && !disableNetworkTime) {
  6. t.traceBegin("StartNetworkTimeUpdateService");
  7. try {
  8. networkTimeUpdater = new NetworkTimeUpdateService(context);
  9. ServiceManager.addService("network_time_update_service",
  10. networkTimeUpdater);
  11. } catch (Throwable e) {
  12. reportWtf("starting NetworkTimeUpdate service", e);
  13. }
  14. }
  15. //这边是启动时间同步服务
  16. final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
  17. networkTimeUpdaterF.systemRunning();

二、同步时间的定时器设置(重试逻辑)

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

1、注册定时器广播接收器 

  1. //首先在这儿注册一个广播接收器,接收定时器发过来的所有时间同步定时广播
  2. private void registerForAlarms() {
  3. mContext.registerReceiver(
  4. new BroadcastReceiver() {
  5. @Override
  6. public void onReceive(Context context, Intent intent) {
  7. mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
  8. }
  9. }, new IntentFilter(ACTION_POLL));
  10. }
  11. //接收到定时器广播后,就处理定时器事件
  12. private class MyHandler extends Handler {
  13. public void handleMessage(Message msg) {
  14. switch (msg.what) {
  15. case EVENT_AUTO_TIME_ENABLED:
  16. case EVENT_POLL_NETWORK_TIME:
  17. case EVENT_NETWORK_CHANGED:
  18. onPollNetworkTime(msg.what);
  19. break;
  20. }
  21. }
  22. }

2、时间同步间隔的设置

  1. mPollingIntervalMs = mContext.getResources().getInteger(
  2. com.android.internal.R.integer.config_ntpPollingInterval);
  3. mPollingIntervalShorterMs = mContext.getResources().getInteger(
  4. com.android.internal.R.integer.config_ntpPollingIntervalShorter);
  5. mTryAgainTimesMax = mContext.getResources().getInteger(
  6. com.android.internal.R.integer.config_ntpRetry);

com.android.internal.R.integer.config_ntpPollingInterval这个是在另一个资源文件里获取的,在frameworks/base/core/res/res/values/config.xml里

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

  1. <!-- Remote server that can provide NTP responses. -->
  2. <string translatable="false" name="config_ntpServer">ntp.aliyun.com</string>
  3. <!-- Normal polling frequency in milliseconds -->
  4. <integer name="config_ntpPollingInterval">300000</integer>
  5. <!-- Try-again polling interval in milliseconds, in case the network request failed -->
  6. <integer name="config_ntpPollingIntervalShorter">60000</integer>
  7. <!-- Number of times to try again with the shorter interval, before backing
  8. off until the normal polling interval. A value < 0 indicates infinite. -->
  9. <integer name="config_ntpRetry">3</integer>
  10. <!-- Timeout to wait for NTP server response in milliseconds. -->
  11. <integer name="config_ntpTimeout">5000</integer>

 在这个config.xml文件里,

可以设置ntpserver,因为默认的地址在国内不能访问

config_ntpPollingInterval,这个配置是一个通用轮训间隔,默认是86400000,是24小时,我这儿改为了300秒暂时

config_ntpPollingIntervalShorter,这个是短时间轮训间隔

config_ntpRetry,这个是短时间轮训间隔的次数

3、时间同步时间间隔的逻辑

  1. //NetworkTimeUpdateService构造函数定义了一个pollIntent,可以用于下面resetAlarm来设置定时器的发送广播的intent,这样在定时器时间来临时,定时器发送广播这边便可以知道已经到达定时时间
  2. public NetworkTimeUpdateService(Context context) {
  3. Intent pollIntent = new Intent(ACTION_POLL, null);
  4. mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
  5. }
  6. /**
  7. * Cancel old alarm and starts a new one for the specified interval.
  8. * @param interval when to trigger the alarm, starting from now.
  9. */
  10. private void resetAlarm(long interval) {
  11. mAlarmManager.cancel(mPendingPollIntent);
  12. long now = SystemClock.elapsedRealtime();
  13. long next = now + interval;
  14. mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
  15. }
  16. //这边的处理是短时间多次轮询和长时间轮询的处理
  17. //刚开机时,由于网络较系统启动较慢,所以需要多次轮询,轮询间隔也会比较小
  18. //所以这里mPollingIntervalShorterMs是短时间轮询,设置定时器就用resetAlarm方法就行
  19. //每次接收到定时器广播后,这边的mTryAgainCounter会加1,当短时间轮询超过一定次数,默认为3,
  20. //就会切换为长时间轮询mPollingIntervalMs
  21. private void onPollNetworkTimeUnderWakeLock(int event) {
  22. if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
  23. } else {
  24. mTryAgainCounter++;
  25. if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
  26. resetAlarm(mPollingIntervalShorterMs);
  27. } else {
  28. // Try much later
  29. mTryAgainCounter = 0;
  30. resetAlarm(mPollingIntervalMs);
  31. }
  32. }
  33. }

4、同步时间

当定时器到时间时,会去真正的NtpTrustedTime类里同步时间,mTime.forceRefresh() 会去进行时间同步

  1. private final NtpTrustedTime mTime;
  2. private void onPollNetworkTimeUnderWakeLock(int event) {
  3. NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
  4. if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
  5. mTime.forceRefresh();
  6. cachedNtpResult = mTime.getCachedTimeResult();
  7. }
  8. }

三、时间同步请求

1、获取ntp时间,并且设置系统时间

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

同步时间的操作在forceRefresh方法里,首先获取getNtpConnectionInfo(),获取相关的配置,

包括serverName和timeoutMillis,serverName默认是com.android,国内无法访问,我这里设

置为了ntp.aliyun.com,具体细节可以参照第二节第2小节。

然后是创建一个SntpClient,通过sntp获取到的时间,创建一个TimeResult对象,这个对象可以通过getCachedTimeResult()函数获取到

public TimeResult getCachedTimeResult() {

        return mTimeResult;

}

  1. frameworks/base/core/java/android/util/NtpTrustedTime.java
  2. public boolean forceRefresh() {
  3. NtpConnectionInfo connectionInfo = getNtpConnectionInfo();
  4. final SntpClient client = new SntpClient();
  5. final String serverName = connectionInfo.getServer();
  6. final int timeoutMillis = connectionInfo.getTimeoutMillis();
  7. Log.d(TAG, "forceRefresh, serverName = " + serverName);
  8. if (client.requestTime(serverName, timeoutMillis, network)) {
  9. long ntpCertainty = client.getRoundTripTime() / 2;
  10. mTimeResult = new TimeResult(
  11. client.getNtpTime(), client.getNtpTimeReference(), ntpCertainty);
  12. Log.d(TAG, "]forceRefresh()-->requestTime true");
  13. return true;
  14. } else {
  15. Log.d(TAG, "forceRefresh()-->requestTime false");
  16. return false;
  17. }
  18. }

2、根据拿到的ntp时间设置系统时间

再回到onPollNetworkTimeUnderWakeLock方法,mTime.forceRefresh后,我们看到

再次获取了一下cachedNtpResult,此时cachedNtpResult不再是null,所以下面代码的

if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs)

分支可以执行到,里面调用timeSuggestion将获取到的ntp时间设置到系统时间里

  1. frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
  2. private void onPollNetworkTimeUnderWakeLock(int event) {
  3. NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
  4. if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
  5. mTime.forceRefresh();
  6. cachedNtpResult = mTime.getCachedTimeResult();
  7. }
  8. if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
  9. resetAlarm(mPollingIntervalMs);
  10. // Suggest the time to the time detector. It may choose use it to set the system clock.
  11. TimestampedValue<Long> timeSignal = new TimestampedValue<>(
  12. cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis());
  13. NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
  14. timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateService. event=" + event);
  15. mTimeDetector.suggestNetworkTime(timeSuggestion);
  16. } else {}
  17. }

3、Sntp获取ntp服务器的时间

ntp端口123,在SntpClient里,会组一个udp包向ntpserver发送,并且阻塞的等待返回结果

  1. public boolean requestTime(InetAddress address, int port, int timeout, Network network) {
  2. DatagramSocket socket = null;
  3. final int oldTag = TrafficStats.getAndSetThreadStatsTag(
  4. TrafficStatsConstants.TAG_SYSTEM_NTP);
  5. socket = new DatagramSocket();
  6. network.bindSocket(socket);
  7. socket.setSoTimeout(timeout);
  8. byte[] buffer = new byte[NTP_PACKET_SIZE];
  9. DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, port);
  10. // set mode = 3 (client) and version = 3
  11. // mode is in low 3 bits of first byte
  12. // version is in bits 3-5 of first byte
  13. buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
  14. // get current time and write it to the request packet
  15. final long requestTime = System.currentTimeMillis();
  16. final long requestTicks = SystemClock.elapsedRealtime();
  17. writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);
  18. socket.send(request);
  19. // read the response
  20. DatagramPacket response = new DatagramPacket(buffer, buffer.length);
  21. socket.receive(response);
  22. }

四、更改时区

Android11 如何修改默认时区_路过独木桥!!的博客-CSDN博客_android 设置默认时区

在build/make/core/main.mk文件里加一个属性

五、ntp原理

 

系统时钟同步的工作过程如下:

Device A发送一个NTP报文给Device B,该报文带有它离开Device A时的时间戳,该时间戳为10:00:00am(T1)。

当此NTP报文到达Device B时,Device B加上自己的时间戳,该时间戳为11:00:01am(T2)。

当此NTP报文离开Device B时,Device B再加上自己的时间戳,该时间戳为11:00:02am(T3)。

当Device A接收到该响应报文时,Device A的本地时间为10:00:03am(T4)。

至此,Device A已经拥有足够的信息来计算两个重要的参数:

NTP报文的往返时延Delay=(T4-T1)-(T3-T2)=2秒。

Device A相对Device B的时间差offset=((T2-T1)+(T3-T4))/2=1小时。

这样,Device A就能够根据这些信息来设定自己的时钟,使之与Device B的时钟同步。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/808191
推荐阅读
相关标签
  

闽ICP备14008679号