Android系统时间之网络更新时间机制

NetworkTimeUpdateService

Posted by 夏敏的博客 on March 12, 2018

本文分析基于AOSP 8.0源码:在线网址为: 8.0.0_r4

Android时间更新的服务为:NetworkTimeUpdateService,位于 frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java

public class NetworkTimeUpdateService extends Binder

该类设计的比较简单,我们来看看其是如何工作的

首先要知道的是,该类运行在SystemServer进程中,我们可以在SystemServer中看到,以下代码:

NetworkTimeUpdateService networkTimeUpdater = null;

// 初始化NetworkTimeUpdateService,走其构造方法
networkTimeUpdater = new NetworkTimeUpdateService(context);
ServiceManager.addService("network_time_update_service", networkTimeUpdater);

//调用service的systemRunning方法
try {
    if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
} catch (Throwable e) {
    reportWtf("Notifying NetworkTimeService running", e);
}

可以看到,SystemServer对其进行了初始化,还调用了systemRunning方法。

继续去看看其构造方法,和systemRunning,先看下够造方法:

103    public NetworkTimeUpdateService(Context context) {
104        mContext = context;
105        mTime = NtpTrustedTime.getInstance(context);
106        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
107        Intent pollIntent = new Intent(ACTION_POLL, null);
           //时间同步有可能超时,使用该PendingIntent进行(间隔再次发起)时间同步。
108        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
109        // poll间隔
110        mPollingIntervalMs = mContext.getResources().getInteger(
111                com.android.internal.R.integer.config_ntpPollingInterval);
           //时间同步超时,再次发起时间同步请求。
112        mPollingIntervalShorterMs = mContext.getResources().getInteger(
113                com.android.internal.R.integer.config_ntpPollingIntervalShorter);
114        mTryAgainTimesMax = mContext.getResources().getInteger(
115                com.android.internal.R.integer.config_ntpRetry);
116        mTimeErrorThresholdMs = mContext.getResources().getInteger(
117                com.android.internal.R.integer.config_ntpThreshold);
118
119        mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
120                PowerManager.PARTIAL_WAKE_LOCK, TAG);
121    }

看上面的代码,我们可以看到:

  1. 调用了 NtpTrustedTime.getInstance(context); 等下去看干嘛的
  2. 初始化一个时间同步的pendingIntent
  3. 初始化一些可以OEM的时间同步相关参数

那么看下 NtpTrustedTime.getInstance(context); 看这个名字叫:可信时间 frameworks/base/core/java/android/util/NtpTrustedTime.java

52    private NtpTrustedTime(String server, long timeout) {
53        if (LOGD) Log.d(TAG, "creating NtpTrustedTime using " + server);
54        mServer = server;
55        mTimeout = timeout;
56    }
57
58    public static synchronized NtpTrustedTime getInstance(Context context) {
59        if (sSingleton == null) {
60            final Resources res = context.getResources();
61            final ContentResolver resolver = context.getContentResolver();
62
63            final String defaultServer = res.getString(
64                    com.android.internal.R.string.config_ntpServer);
65            final long defaultTimeout = res.getInteger(
66                    com.android.internal.R.integer.config_ntpTimeout);
67
68            final String secureServer = Settings.Global.getString(
69                    resolver, Settings.Global.NTP_SERVER);
70            final long timeout = Settings.Global.getLong(
71                    resolver, Settings.Global.NTP_TIMEOUT, defaultTimeout);
72
73            final String server = secureServer != null ? secureServer : defaultServer;
74            sSingleton = new NtpTrustedTime(server, timeout);
75            sContext = context;
76        }
77
78        return sSingleton;
79    }

我们可以看到,这个类是用来管理增加时间同步服务器的。那我们继续回到NetworkTimeUpdateService的systemRunning。

122
123    /** Initialize the receivers and initiate the first NTP request */
124    public void systemRunning() {
           // 注册来自Telephony Ril的广播,moderm时间会通过这个方法更新
125        registerForTelephonyIntents();
           //  是配合第“一”中介绍的mPendingPollIntent 来工作的,主要作用是构造handler Message并再次发起时间同步请求。
126        registerForAlarms();
           // 监听网络状态变化,以便于在连接网络后更新时间
127        registerForConnectivityIntents();
128
129        HandlerThread thread = new HandlerThread(TAG);
130        thread.start();
131        mHandler = new MyHandler(thread.getLooper());
132        // Check the network time on the new thread
133        mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
134        // 注册SettingsObserver, 在设置里点击了自动时间的开关就会触发这儿
135        mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
136        mSettingsObserver.observe(mContext);
137    }

我们可以看到,发了一个请求网络时间的请求给Handler,那么Handler 怎么处理的呢。

282    /** Handler to do the network accesses on */
283    private class MyHandler extends Handler {
284
285        public MyHandler(Looper l) {
286            super(l);
287        }
288
289        @Override
290        public void handleMessage(Message msg) {
291            switch (msg.what) {
292                case EVENT_AUTO_TIME_CHANGED:
293                case EVENT_POLL_NETWORK_TIME:
294                case EVENT_NETWORK_CHANGED:
295                    onPollNetworkTime(msg.what);
296                    break;
297            }
298        }
299    }

三个请求都会去走onPollNetworkTime方法,这些请求由:数据库变换,网络状态变化等状态改变发出的。

162    private void onPollNetworkTime(int event) {
163        // If Automatic time is not set, don't bother.
164        if (!isAutomaticTimeRequested()) return;
165        mWakeLock.acquire();
166        try {
167            onPollNetworkTimeUnderWakeLock(event);
168        } finally {
169            mWakeLock.release();
170        }
171    }
172
173    private void onPollNetworkTimeUnderWakeLock(int event) {
174        final long refTime = SystemClock.elapsedRealtime();
175        // If NITZ time was received less than mPollingIntervalMs time ago,
176        // no need to sync to NTP.
           // mNitzTimeSetTime 来自Moderm,如果当前时间刚通过moderm更新不久,小于我们设定的阈值时间,则不进行时间同步。
177        if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
178            resetAlarm(mPollingIntervalMs);
179            return;
180        }

181        final long currentTime = System.currentTimeMillis();
182        if (DBG) Log.d(TAG, "System time = " + currentTime);
183        // Get the NTP time
           // 如果机器刚启动,或者机器运行时间大于mPollingIntervalMs,即10天
           // 或者设置等发起的主动更新时间请求,则发起网络时间同步请求。否则,10天后再进行时间同步。
184        if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
185                || event == EVENT_AUTO_TIME_CHANGED) {
186            if (DBG) Log.d(TAG, "Before Ntp fetch");
187             
188            // force refresh NTP cache when outdated
189            if (mTime.getCacheAge() >= mPollingIntervalMs) {
190                mTime.forceRefresh();   //遍历时间服务器,发起时间同步
191            }
192             
               //获取最新同步的时间缓冲数据,如无,则再次发起时间同步,间隔时间为mPollingIntervalShorterMs,即30秒。
193            // only update when NTP time is fresh
194            if (mTime.getCacheAge() < mPollingIntervalMs) {
195                final long ntp = mTime.currentTimeMillis();
196                mTryAgainCounter = 0;
197                // If the clock is more than N seconds off or this is the first time it's been
198                // fetched since boot, set the current time.
                   // 如果开机第一次同步或者最新时间与当前时间差别超过mTimeErrorThresholdMs即25
                   //则进行时间设定。否则认定新同步时间与当前时间差别不大,不覆盖当前时间。
199                if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
200                        || mLastNtpFetchTime == NOT_SET) {
201                    // Set the system time
202                    if (DBG && mLastNtpFetchTime == NOT_SET
203                            && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
204                        Log.d(TAG, "For initial setup, rtc = " + currentTime);
205                    }
206                    if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
207                    // Make sure we don't overflow, since it's going to be converted to an int
208                    if (ntp / 1000 < Integer.MAX_VALUE) {
209                        SystemClock.setCurrentTimeMillis(ntp);
210                    }
211                } else {
212                    if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
213                }
214                mLastNtpFetchTime = SystemClock.elapsedRealtime();
215            } else {
                   // 重新获取时间,30秒后进行时间同步,否则,10天后更新。
216                // Try again shortly
217                mTryAgainCounter++;
218                if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
219                    resetAlarm(mPollingIntervalShorterMs);
220                } else {
221                    // Try much later
222                    mTryAgainCounter = 0;
223                    resetAlarm(mPollingIntervalMs);
224                }
225                return;
226            }
227        }
           // 如果刚更新时间不久,则10天后再发起时间同步请求。
228        resetAlarm(mPollingIntervalMs);
229    }

190行刚刚循环获取时间的forceRefresh,NtpTrustedTime的代码,从代码中可以看到,是使用SNTP获取时间的,连上了我们的第二讲中的SNTP

81    @Override
82    public boolean forceRefresh() {
83        if (TextUtils.isEmpty(mServer)) {
84            // missing server, so no trusted time available
85            return false;
86        }
87
88        // We can't do this at initialization time: ConnectivityService might not be running yet.
89        synchronized (this) {
90            if (mCM == null) {
91                mCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
92            }
93        }
94
95        final NetworkInfo ni = mCM == null ? null : mCM.getActiveNetworkInfo();
96        if (ni == null || !ni.isConnected()) {
97            if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");
98            return false;
99        }
100
101
102        if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
103        final SntpClient client = new SntpClient();
           // SNTP协议更新时间
104        if (client.requestTime(mServer, (int) mTimeout)) {
105            mHasCache = true;
106            mCachedNtpTime = client.getNtpTime();
107            mCachedNtpElapsedRealtime = client.getNtpTimeReference();
108            mCachedNtpCertainty = client.getRoundTripTime() / 2;
109            return true;
110        } else {
111            return false;
112        }
113    }

关于SNTP协议里面的,暂时就不讲了。我们的分析到此基本就够了,有兴趣的可以继续追。


谢谢大家的阅读

作者:Anderson大码渣,欢迎关注我的简书: Anderson大码渣