Android设备NTP 时间同步流程

4,121 阅读5分钟

Android设备时间同步主要通过两种方式,非移动设备这里只讨论NTP

  • NITZ:Network Identity and Time Zone(网络标识和时区),是一种用于自动配置本地的时间和日期的机制,需要运营商支持,可从运营商获取时间和时区具体信息。

  • NTP:Network Time Protocol(网络时间协议),用来同步网络中各个计算机的时间的协议。在手机中,NTP更新时间的方式是通过GPRS或wifi向特定服务器获取时间信息(不包含时区信息)

涉及的主要服务

frameworks/base/services/core/java/com/android/server/NewNetworkTimeUpdateService.java
frameworks/base/core/java/android/util/NtpTrustedTime.java
frameworks/base/services/core/java/com/android/server/AlarmManagerService.java
frameworks/base/core/java/android/net/SntpClient.java

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

config文件要到考虑overlay,对应下面代码的各项默认值

  
    <string translatable="false" name="config_ntpServer">ntp.aliyun.com</string>
    <integer name="config_ntpPollingInterval">86400000</integer>
    <integer name="config_ntpPollingIntervalShorter">8000</integer>
    <integer name="config_ntpRetry">3</integer>
    <integer name="config_ntpThreshold">5000</integer>
    <integer name="config_ntpTimeout">5000</integer>

日志分析

06-28 05:40:21.423  2041  2041 I SystemServer: StartAlarmManagerService
06-28 05:40:21.427  2041  2041 D AlarmManagerService: Kernel timezone updated to -480 minutes west of GMT
06-28 05:40:21.427  2041  2041 I AlarmManager: Current time only 125427, advancing to build time 1687902002000   //设置为rom编译时间
06-28 05:40:21.427  2041  2041 I AlarmManager: xxx_time_sync  setKernelTime millis = 1687902002000
06-28 05:40:21.427  2041  2041 D AlarmManagerService: Setting time of day to sec=1687902002     //jni调用 AlarmManagerService.cpp 
06-28 05:40:21.821  2041  2041 I SystemServer: StartNetworkTimeUpdateService
06-28 05:40:21.822  2041  2041 I NetworkTimeUpdateService: xxx_time_sync  mPollingIntervalMs = 86400000 mPollingIntervalShorterMs = 8000 mTryAgainTimesMax = 3 mTimeErrorThresholdMs=5000
06-28 05:40:21.822  2041  2041 D SystemServer: Using networkTimeUpdater class=class com.android.server.NewNetworkTimeUpdateService //用NewNetworkTimeUpdateService
06-28 05:40:21.823  2041  2041 D SystemServerTiming: StartNetworkTimeUpdateService took to complete: 2ms
06-28 05:41:48.944  2041  2606 I NetworkTimeUpdateService: New default network 100; checking time.  //联网
06-28 05:41:48.945  2041  2606 I NetworkTimeUpdateService: Stale NTP fix; forcing refresh           
06-28 10:11:24.014  2041  2606 D SntpClient: round trip: 36ms, clock offset: 16175362ms   //sntp request请求时间
06-28 10:11:24.014  2041  2606 I NtpTrustedTime: xxx_time_sync  SntpClient requestTime  mCachedNtpTime = 1687918284376
06-28 10:11:24.015  2041  2606 I NtpTrustedTime: currentTimeMillis() cache hit
06-28 10:11:24.016  2041  2606 I NtpTrustedTime: currentTimeMillis() cache hit
06-28 10:11:24.017  2041  2606 I NtpTrustedTime: currentTimeMillis() cache hit
06-28 10:11:24.017  2041  2606 I NetworkTimeUpdateService: xxx_time_sync  updateSystemClock currentTimeMillis = 1687918284378  forceUpdate =false
06-28 10:11:24.019  2041  2606 I SystemClock: xxx_time_sync  setCurrentTimeMillis millis = 1687918284379
06-28 10:11:24.020  2041  2606 I AlarmManager: xxx_time_sync  setTimeImpl millis = 1687918284379  currentTimeMillis =1687902109019
06-28 10:11:24.020  2041  2606 I AlarmManager: xxx_time_sync  setKernelTime millis = 1687918284379

从日志可以看到AlarmManagerService先被启动。在AlarmManagerService onStart()会有一段check时间的逻辑。即选取使用当前时间、根文件系统的时间戳和ro.build.date.utc系统属性的值(以秒为单位)中的最近时间。

            // Ensure that we're booting with a halfway sensible current time.  Use the
            // most recent of Build.TIME, the root file system's timestamp, and the
            // value of the ro.build.date.utc system property (which is in seconds).
            
            
            final long systemBuildTime =  Long.max(
                    1000L * SystemProperties.getLong("ro.build.date.utc", -1L),
                    Long.max(Environment.getRootDirectory().lastModified(), Build.TIME));
            if (mInjector.getCurrentTimeMillis() < systemBuildTime) {
                Slog.i(TAG, "Current time only " + mInjector.getCurrentTimeMillis()
                        + ", advancing to build time " + systemBuildTime);
                mInjector.setKernelTime(systemBuildTime);
            }

上面部分代码对应以下log。前者时间为1970年,后者为ro.build.date.utc的时间。

06-28 05:40:21.427 2041 2041 I AlarmManager: Current time only 125427, advancing to build time 1687902002000

NewNetworkTimeUpdateService Android10版本

NetworkTimeUpdateService是用于处理ntp同步的服务。在Android10版本,有NewNetworkTimeUpdateService/OldNetworkTimeUpdateService两个服务分别实现NetworkTimeUpdateService。从SystemServer中可以判断真正使用的是NewNetworkTimeUpdateService。

继续看代码主要分析 构造方法以及/systemRunning()

public NewNetworkTimeUpdateService(Context context) {
        mContext = context;
        mTime = NtpTrustedTime.getInstance(context);
        mAlarmManager = mContext.getSystemService(AlarmManager.class);
        mCM = mContext.getSystemService(ConnectivityManager.class);

        Intent pollIntent = new Intent(ACTION_POLL, null);
        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);

        mPollingIntervalMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpPollingInterval);
        mPollingIntervalShorterMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpPollingIntervalShorter);
        mTryAgainTimesMax = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpRetry);
        mTimeErrorThresholdMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpThreshold);

        mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
                PowerManager.PARTIAL_WAKE_LOCK, TAG);
    }
    public void systemRunning() {
        registerForTelephonyIntents();
        registerForAlarms();

        HandlerThread thread = new HandlerThread(TAG);
        thread.start();
        mHandler = new MyHandler(thread.getLooper());
        mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
        mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);

        mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
        mSettingsObserver.observe(mContext);
    }

主要分析

  • registerForAlarms();
  • mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
  • mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);

ACTION_POLL用于绑定mPendingPollIntent最终注册在AlarmManagerService中,定时校验时间。 对应的event,EVENT_POLL_NETWORK_TIME。

  private static final String ACTION_POLL =
            "com.android.server.NetworkTimeUpdateService.action.POLL";
            
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));
    }

mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler) 通过ConnectivityManager注册网络回调,如果是available,则触发event,EVENT_NETWORK_CHANGED。

        @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;
        }
    }

mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);

对应设置中自动时间同步的swtich开关

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

onPollNetworkTime处理EVENT

只有在网络变化、自动更新时间设置变化、定时任务三种情况才会触发时间同步。(不一定同步成功。)

   private class MyHandler extends Handler {

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

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case EVENT_AUTO_TIME_CHANGED:
                case EVENT_POLL_NETWORK_TIME:
                case EVENT_NETWORK_CHANGED:
                    onPollNetworkTime(msg.what);
                    break;
            }
        }
    }
    
     private void onPollNetworkTime(int event) {
        // If Automatic time is not set, don't bother. Similarly, if we don't
        // have any default network, don't bother.
        if (mDefaultNetwork == null) return;  //没有网络就不校正
        mWakeLock.acquire();
        try {
            onPollNetworkTimeUnderWakeLock(event);
        } finally {
            mWakeLock.release();
        }
    }

    

重点分析onPollNetworkTimeUnderWakeLock

  • mTime即NtpTrustedTime
  • mPollingIntervalMs 即 config_ntpPollingInterval 默认 86400000 = 24小时
  • mTryAgainTimesMax config_ntpRetry 默认3次
  • mPollingIntervalShorterMs config_ntpPollingIntervalShorter 默认60s
 private void onPollNetworkTimeUnderWakeLock(int event) {
 
        // Force an NTP fix when outdated
        //同步时间超过24小时则强制刷新
        if (mTime.getCacheAge() >= mPollingIntervalMs) {//24小时
            if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
            mTime.forceRefresh();
        }
         
         //小于24小时 更新24小时
        if (mTime.getCacheAge() < mPollingIntervalMs) {
            // Obtained fresh fix; schedule next normal update
            resetAlarm(mPollingIntervalMs); //重新计时
            if (isAutomaticTimeRequested()) {//Settings.Global.AUTO_TIME 开关是否打开
                updateSystemClock(event);
            }

        } else {
            // No fresh fix; schedule retry
            mTryAgainCounter++;
            if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
                resetAlarm(mPollingIntervalShorterMs);  //60s 短定时任务
            } else {
                // Try much later
                mTryAgainCounter = 0;
                resetAlarm(mPollingIntervalMs);
            }
        }
    }

getCacheAge()如何计算

  • mHasCache forceRefresh才为true ,默认是false。
  • elapsedRealtime开机启动开始计算的时间 。
  • mCachedNtpElapsedRealtime forceRefresh的时候 通过SntpClient获取的时间。

所以getCacheAge()理解成为距离上次强制更新时间的间隔

    public long getCacheAge() {
        if (mHasCache) {
            return SystemClock.elapsedRealtime() - mCachedNtpElapsedRealtime;
        } else {
            return Long.MAX_VALUE;
        }
    }

forceRefresh()

二次检查connectivity状态,这里代码省略。

  • mServer 这里是config文件中对应的ntpserver地址,我们是aliyun
  • mTimeout这里的timeout是上边传下来的。
  • 如果ntpserver正常 网络连接正常通过stnpClient requestTime更新时间,具体通过socket
frameworks/base/core/java/android/util/NtpTrustedTime.java
   public boolean forceRefresh(Network network) {
        if (TextUtils.isEmpty(mServer)) { //ntpserver为空 不更新了
            // missing server, so no trusted time available
            return false;
        }

        // We can't do this at initialization time: ConnectivityService might not be running yet.
        synchronized (this) {
            if (mCM == null) {
                mCM = sContext.getSystemService(ConnectivityManager.class);
            }
        }

        final NetworkInfo ni = mCM == null ? null : mCM.getNetworkInfo(network);
        if (ni == null || !ni.isConnected()) {//网络没连上不更新了
            if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");
            return false;
        }


        if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
        final SntpClient client = new SntpClient(); //stpnClient 请求更新
        if (client.requestTime(mServer, (int) mTimeout, network)) {
            mHasCache = true;
            mCachedNtpTime = client.getNtpTime();//刷新ntp时间
            mCachedNtpElapsedRealtime = client.getNtpTimeReference();
            mCachedNtpCertainty = client.getRoundTripTime() / 2;
            return true;
        } else {
            return false;
        }
    }

updateSystemClock

SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis()) 最终通过alramManager setTime()更新时间

  private void updateSystemClock(int event) {
        final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
        if (!forceUpdate) {
            if (getNitzAge() < mPollingIntervalMs) {
                if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
                return;
            }

            final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
            if (skew < mTimeErrorThresholdMs) {
                if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
                return;
            }
        }
        SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
    }

以上为Android10更新逻辑

Android11 12的改变

11 12的版本,只有一个NetworkTimeUpdateService。其中最大的差别是在onPollNetworkTimeUnderWakeLock()

依旧是对比mPollingIntervalMs,超过(默认24小时强制刷新) 更新时间则通过mTimeDetector.suggestNetworkTime(timeSuggestion);等等一系列复杂的逻辑最终还是调用AlarmManager设置时间。 可以读下代码,更复杂更科学。因为和我在追的问题无关,所以不深入了。

 private void onPollNetworkTimeUnderWakeLock(int event) {
        // Force an NTP fix when outdated
        NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
        if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {// 超过24小时刷新
            if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
            mTime.forceRefresh();
            cachedNtpResult = mTime.getCachedTimeResult();
        }

        if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
            // Obtained fresh fix; schedule next normal update
            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 {
            // No fresh fix; schedule retry
            mTryAgainCounter++;
            if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
                resetAlarm(mPollingIntervalShorterMs);
            } else {
                // Try much later
                mTryAgainCounter = 0;
                resetAlarm(mPollingIntervalMs);
            }
        }
    }

总结

Android设备默认时间首先通过AlarmManagerService根据buildtime,根目录文件修改时间,当前时间选一个最接近的设为默认时间。然后根据ntp策略进行时间同步。

对于非移动Android设备来说,只有在三种情况下会触发ntp同步。

  1. 网络变化avaiable的时候
  2. 自动时间同步switch改变的时候
  3. 定时任务启动的时候 一般默认24小时

juejin.cn/post/684490… blog.csdn.net/csdn_of_cod…