Android系统如何通过NTP协议自动同步时间,以及NTP服务的关键代码逻辑

移动开发 Android
NTP 的主要目标是提供高准确性的时间。NTP 版本 4 (NTPv4) 可以提供毫秒级的精度,而更先进的实现(如 RFC 5905 中定义的 NTP Secure 或 Precision Time Protocol, PTP)可以提供微秒或纳秒级的精度。

NTP网络时间协议

NTP (Network Time Protocol) 网络时间协议用于同步计算机系统之间的时钟。NTP允许计算机时钟与参考时钟源(如原子钟、GPS 接收器或其他 NTP 服务器)进行同步,时间的准确性对于金融交易、网络通信、科学研究和安全系统等应用来说至关重要。

NTP协议特点和功能:

  1. 「准确性」:NTP 的主要目标是提供高准确性的时间。NTP 版本 4 (NTPv4) 可以提供毫秒级的精度,而更先进的实现(如 RFC 5905 中定义的 NTP Secure 或 Precision Time Protocol, PTP)可以提供微秒或纳秒级的精度。
  2. 「层次结构」:NTP 使用层次结构来组织服务器。时间信息从参考时钟源(称为“一级”服务器)流向“二级”服务器,再流向“三级”服务器,以此类推。每个服务器都会增加一定的延迟和误差,NTP 算法会尽力补偿这些误差。
  3. 「时间同步」:NTP 通过交换时间戳和延迟测量来同步时间。NTP 客户端向服务器发送一个时间请求,服务器响应时附带当前时间戳。客户端然后计算往返延迟,使用这些信息来调整其本地时钟。
  4. 「可伸缩性」:NTP 可以在各种规模的网络中运行,从小型局域网到全球互联网。NTP 服务器可以支持大量客户端,并且通过负载均衡和冗余来提高可靠性和可用性。
  5. 「安全性」:NTP 的安全性是一个重要问题,因为恶意服务器可能尝试提供错误的时间信息。NTP Secure (RFC 5905) 提供了一种加密和认证机制,可以确保时间信息的完整性和真实性。
  6. 「广播和多播」:NTP 支持广播和多播模式,大量客户端可以同时从单个服务器获取时间信息。
  7. 「兼容性」:NTP 与之前的互联网时间同步协议(如 ICMP 时间戳和 Daytime 协议)兼容,并提供了更高级别的准确性和功能。

Android NTP时间同步机制

Android系统的NTP时间同步通过访问Internet上的NTP服务器来自动更新时间。基本原理是通过NTP协议与NTP服务器进行通信,获取当前准确的时间,然后同步到本地设备中。NTP服务器会返回一个时间戳,时间戳表示NTP服务器所认为的当前标准时间,Android设备可以通过与本地系统时间进行比较,得出本地设备对准确时间的偏差,并进行时间校正。

自动同步时间时区配置:

  • 进入“设置”->“日期和时间”->“使用网络提供的时间”菜单。
  • 打开“自动确定时区”和“自动确定日期和时间”选项,让设备自动选择最佳的NTP服务器进行同步。

源码分析

NTP是Android原生通过网络获取时间的机制,关键代码逻辑在NetworkTimeUpdateService,NetworkTimeUpdateService是Android系统服务,由SystemServer启动。

「初始化」

// frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
public NetworkTimeUpdateService(Context context) {
    mContext = context;
    //NtpTrustedTime用于获取网络时间
    mTime = NtpTrustedTime.getInstance(context);
    mAlarmManager = mContext.getSystemService(AlarmManager.class);
    mTimeDetector = mContext.getSystemService(TimeDetector.class);
    mCM = mContext.getSystemService(ConnectivityManager.class);

    Intent pollIntent = new Intent(ACTION_POLL, null);
    //创建mPendingPollIntent,用于发送定时广播
    mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
    // 请求服务器频率 86400000ms = 24h
    //从配置文件LINUX/android/frameworks/base/core/res/res/values/config.xml中解析获得
    mPollingIntervalMs = mContext.getResources().getInteger(com.android.internal.R.integer.config_ntpPollingInterval);
    //请求时间间隔60000ms = 10min
    mPollingIntervalShorterMs = mContext.getResources().getInteger(com.android.internal.R.integer.config_ntpPollingIntervalShorter);
    //最大尝试次数 3        
    mTryAgainTimesMax = mContext.getResources().getInteger(com.android.internal.R.integer.config_ntpRetry);

    mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
}

构造方法中主要是对各种变量进行初始化操作,NetworkTimeUpdateService在SystemServer.java创建时,被SystemServer调用。

// frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
/** Initialize the receivers and initiate the first NTP request */
public void systemRunning() {
    //注册广播
    registerForAlarms();
    //初始化Handler
    HandlerThread thread = new HandlerThread(TAG);
    thread.start();
    mHandler = new MyHandler(thread.getLooper());
    //向ConnectivityManager注册网络状况监听
    mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
    mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
    //使用ContentObsrver监听Settings.Global.AUTO_TIME值的变化
    mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler,
            EVENT_AUTO_TIME_ENABLED);
    mAutoTimeSettingObserver.observe();
}

private void registerForAlarms() {
    mContext.registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
        }
    }, new IntentFilter(ACTION_POLL));
}
  • 调用registerForAlarms,注册一个广播接收者,接收ACTION_POLL广播,收到后向消息队列发送一个EVENT_POLL_NETWORK_TIME的事件。
  • 向ConnectivityManager注册网络状况监听。
  • 监听Settings.Global.AUTO_TIME值的变化。

「NetworkTimeUpdateCallback」

// frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
// 定义和注册监听
mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();

mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);

private class NetworkTimeUpdateCallback extends NetworkCallback {
    @Override
    public void onAvailable(Network network) {
        Log.d(TAG, String.format("New default network %s; checking time.", network));
        mDefaultNetwork = network;
        // Running on mHandler so invoke directly.
        onPollNetworkTime(EVENT_NETWORK_CHANGED);
    }

    @Override
    public void onLost(Network network) {
        if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
    }
}

NetworkTimeUpdateCallback实现了NetworkCallback接口,当回调onAvailable(网络切换/可用)时,赋值mDefaultNetwork并调用onPollNetworkTime(EVENT_NETWORK_CHANGED)方法。

「AutoTimeSettingObserver」

// frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler,EVENT_AUTO_TIME_ENABLED);
mAutoTimeSettingObserver.observe();

/**
 * Observer to watch for changes to the AUTO_TIME setting. It only triggers when the setting
 * is enabled.
 */
private static class AutoTimeSettingObserver extends ContentObserver {

    private final Context mContext;
    private final int mMsg;
    private final Handler mHandler;

    AutoTimeSettingObserver(Context context, Handler handler, int msg) {}

    void observe() {
        ContentResolver resolver = mContext.getContentResolver();
        resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),false, this);
    }

    @Override
    public void onChange(boolean selfChange) {
        if (isAutomaticTimeEnabled()) {
            mHandler.obtainMessage(mMsg).sendToTarget();
        }
    }

    /**
     * Checks if the user prefers to automatically set the time.
     */
    private boolean isAutomaticTimeEnabled() {
        ContentResolver resolver = mContext.getContentResolver();
        return Settings.Global.getInt(resolver, Settings.Global.AUTO_TIME, 0) != 0;
    }
}

监听Settings.Global.AUTO_TIME的变化,当值改变且AUTO_TIME != 0 时,向消息队列发送一个类型为EVENT_AUTO_TIME_ENABLED的消息。

「MyHandler」

/** Handler to do the network accesses on */
private class MyHandler extends Handler {

    MyHandler(Looper l) {
        super(l);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case EVENT_AUTO_TIME_ENABLED:
            case EVENT_POLL_NETWORK_TIME:
            case EVENT_NETWORK_CHANGED:
                onPollNetworkTime(msg.what);
                break;
        }
    }
}

Handler发送的所有消息最终都会调用onPollNetworkTime。

「onPollNetworkTime」

private void onPollNetworkTime(int event) {
    // If we don't have any default network, don't bother.
    if (mDefaultNetwork == null) return;
    mWakeLock.acquire();
    try {
        onPollNetworkTimeUnderWakeLock(event);
    } finally {
        mWakeLock.release();
    }
}

当前无网络情况直接返回,并使用PowerManager.WakeLock调用onPollNetworkTimeUnderWakeLock(event)。

private void onPollNetworkTimeUnderWakeLock(int event) {
    // 使用NtpTrustedTime获取网络时间
    NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();

    //cachedNtpResult.getAgeMillis()是上次请求ntp服务器的时间
    //如果大于等于1天,则强制刷新时间
    if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
        if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
        //该方法是个阻塞方法
        mTime.forceRefresh();
        cachedNtpResult = mTime.getCachedTimeResult();
    }

    //cachedNtpResult.getAgeMillis() < 1天
    if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
        //设置定时广播,1天后触发
        resetAlarm(mPollingIntervalMs);

        // Suggest the time to the time detector. It may choose use it to set the system clock.
      // 设置系统时间
        TimestampedValue<Long> timeSignal = new TimestampedValue<>(
                cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis());
        NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
        timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateService. event=" + event);
        mTimeDetector.suggestNetworkTime(timeSuggestion);
    } else {
        mTryAgainCounter++;
        if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
            //设置定时广播,10分钟后触发
            resetAlarm(mPollingIntervalShorterMs);
        } else {
            //设置定时广播,1天后触发
            mTryAgainCounter = 0;
            resetAlarm(mPollingIntervalMs);
        }
    }
}

调用了resetAlarm(),该方法定时发送ACTION_POLL广播。

private void resetAlarm(long interval) {
    mAlarmManager.cancel(mPendingPollIntent);
    long now = SystemClock.elapsedRealtime();
    long next = now + interval;
    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
}

NetworkTimeUpdateService启动后有两种方式触发时间更新。

  • 网络发生变化。
  • Settings中的AUTO_TIME开关变化(自动同步日期时区开关)。

最终都会调用onPollNetworkTimeUnderWakeLock去设置时间,根据获取的cachedNtpResult情况设置不同时间的定时广播再去尝试更新时间。

责任编辑:武晓燕 来源: 沐雨花飞蝶
相关推荐

2022-01-27 08:53:31

LinuxNTP同步

2010-08-06 14:46:51

思科路由器NTP服务器配置

2017-01-09 09:20:07

Ubuntu NTP同步

2010-09-02 10:50:17

时间同步服务器

2019-06-10 15:50:54

LinuxNTP命令

2015-05-25 09:13:31

NTP网络时间协议NTP服务器

2010-08-20 10:29:40

NTP

2009-11-26 14:42:03

开启Suse ntp服

2024-10-12 17:23:30

2020-11-26 12:40:26

NTSNTP系统运维

2011-07-20 09:01:33

域控制器ntp服务器

2015-01-27 11:31:06

2016-12-07 10:30:19

NTP网络时间协议

2021-11-26 00:13:19

OracleRAC集群

2018-03-19 19:00:54

2019-11-11 08:43:46

信息安全NTP服务放大攻击

2018-02-23 09:32:13

LinuxUnixntpd

2019-05-07 10:00:03

ChronyNTPLinux

2022-04-30 09:41:14

LinuxNTP服务器

2021-05-27 05:31:06

服务器时间时钟
点赞
收藏

51CTO技术栈公众号