一、NTP介绍
NTP:网络时间协议,英文名称:Network Time Protocol(NTP)是用来使计算机时间同步化的一种协议,
它可以使计算机对其服务器或时钟源(如石英钟,GPS等等)做同步化,它可以提供高精准度的时间校正(LAN上与标准间差小于1毫秒,WAN上几十毫秒),
且可介由加密确认的方式来防止恶毒的协议攻击。NTP的目的是在无序的Internet环境中提供精确和健壮的时间服务。
二、NTP对时机制
Android系统使用NTP自动更新系统时间的触发机制:
1、监听数据库字段AUTO_TIME,也就是开关网络时间选项,当这个字段发生变化的时候,会立即触发一次时间同步
2、网络连接发生变化,当网络接通,会触发一次时间检查和同步
3、定时更新机制,当预定的时间到了,会触发一次时间检查和同步
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;
}
}
三、检查与同步
frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
mPollingIntervalMs = mContext.getResources().getInteger(com.android.internal.R.integer.config_ntpPollingInterval); // 正常获取网络时间间隔,默认86400000秒,也就是24小时
mPollingIntervalShorterMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpPollingIntervalShorter); // 3次快速尝试获取网络时间,目的是防止网络不好,间隔时间 60000秒,也就是一分钟
mTryAgainTimesMax = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpRetry); // 快速尝试获取网络时间次数,默认三次
mTimeErrorThresholdMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpThreshold); // ntp服务器时间与系统时间差阈值,如果大于5秒则更新
以上信息可以通过命令:dumpsys network_time_update_service 查看,例如: PollingIntervalMs: +1d0h0m0s0ms PollingIntervalShorterMs: +1m0s0ms TryAgainTimesMax: 3 TimeErrorThresholdMs: +5s0ms TryAgainCounter: 1 LastNtpFetchTime: +1m28s223ms
private void onPollNetworkTimeUnderWakeLock(int event) {
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) { // 如果开启NITZ 默认使用nitz更新的时间
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) { // 开机没有获取到网络时间,强制执行连接ntp服务器获取时间
mTime.forceRefresh();
}
// 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 // 时间差小于5秒 ,则更新时间
|| 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); // 更新系统时间,LOG为:AlarmManagerService: Setting time of day to sec=1721811969,查看时间是否更新
}
} 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); // 尝试三次 间隔一分钟获取一次还没有获取到时 ,则24小时后再尝试
}
return;
}
}
resetAlarm(mPollingIntervalMs); // 时间已经设置成功,24小时后再获取
}
frameworks/base/core/java/android/util/NtpTrustedTime.java
final String defaultServer = res.getString(com.android.internal.R.string.config_ntpServer); // 默认NTP服务器:2.android.pool.ntp.org
final long defaultTimeout = res.getInteger( com.android.internal.R.integer.config_ntpTimeout); // 连接NTP服务器超时时间默认5秒
final String secureServer = Settings.Global.getString( resolver, Settings.Global.NTP_SERVER); // 可以通过命令动态修改ntp服务器,重启后生效 adb shell settings put global ntp_server ntp2.aliyun.com
final long timeout = Settings.Global.getLong( resolver, Settings.Global.NTP_TIMEOUT, defaultTimeout); // 可以通过命令动态修改ntp服务器超时时间,重启后生效 adb shell settings put global ntp_timeout 50000
final String server = secureServer != null ? secureServer : defaultServer;
sSingleton = new NtpTrustedTime(server, timeout); // 动态配置npt服务器地址 与超时时间
public boolean forceRefresh() {
if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
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;
} else {
return false;
}
}
四、调试
1、确认当前NTP服务器地址,adb shell settings get global ntp_server 为空时 则需要查看代码中默认的服务器。
2、修改NTP服务器:adb shell settings put global ntp_server ntp2.aliyun.com
3、logcat | grep -Ei "NetworkTimeUpdateService|SntpClient|AlarmManagerService" 查看服务及NTP服务器是否异常
4、dumpsys network_time_update_service 查看对时默认配置,比如时间差,快速重试时间,快速重试次数等