最近遇到在安卓TV上连接有线网络时断电上电开机会有系统时间和网络时间不一致(同步网络时间失败)的情况,概率在10%以上。根据需要大致看了一下流程,稍微总结了一下。纯属个人总结,可能有不严谨甚至错误的地方欢迎大家指正。
NTP:一种网络时间协议,用于同步网络中各个终端的系统时间
NTP时间服务器:由于涉及到时间同步,所以时间服务器一般采用高时效的UDP的package方式,存在连接不稳定超时丢包的情况
相关代码:
\frameworks\base\core\res\res\values\config.xml
<!-- Remote server that can provide NTP responses. -->
<string translatable="false" name="config_ntpServer">2.android.pool.ntp.org</string>
<!-- Normal polling frequency in milliseconds -->
<integer name="config_ntpPollingInterval">86400000</integer>
<!-- Try-again polling interval in milliseconds, in case the network request failed -->
<integer name="config_ntpPollingIntervalShorter">60000</integer>
<!-- Number of times to try again with the shorter interval, before backing
off until the normal polling interval. A value < 0 indicates infinite. -->
<integer name="config_ntpRetry">3</integer>
<!-- If the time difference is greater than this threshold in milliseconds,
then update the time. -->
<integer name="config_ntpThreshold">5000</integer>
<!-- Timeout to wait for NTP server response. -->
<integer name="config_ntpTimeout">20000</integer>
\frameworks\base\core\java\android\util\NtpTrustedTime.java:
public boolean forceRefresh() {
if (TextUtils.isEmpty(mServer)) {
// 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 = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
}
}
final NetworkInfo ni = mCM == null ? null : mCM.getActiveNetworkInfo();
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();
if (client.requestTime(mServer, (int) mTimeout)) {
mHasCache = true;
mCachedNtpTime = client.getNtpTime();
mCachedNtpElapsedRealtime = client.getNtpTimeReference();
mCachedNtpCertainty = client.getRoundTripTime() / 2;
return true;
} else {
return false;
}
}
forceRefresh函数里会调用SntpClient.requestTime(mServer, (int) mTimeout)来获取时间 mServer为同步时间服务器, mTimeout为请求超时时间
对应config里的config_ntpServer和config_ntpTimeout
SntpClient.requestTime的方法在:
\frameworks\base\core\java\android\net\SntpClient.java里
/**
* Sends an SNTP request to the given host and processes the response.
*
* @param host host name of the server.
* @param timeout network timeout in milliseconds.
* @return true if the transaction was successful.
*/
public boolean requestTime(String host, int timeout) {
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
socket.setSoTimeout(timeout);
InetAddress address = InetAddress.getByName(host);
byte[] buffer = new byte[NTP_PACKET_SIZE];
DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT);
// set mode = 3 (client) and version = 3
// mode is in low 3 bits of first byte
// version is in bits 3-5 of first byte
buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
// get current time and write it to the request packet
long requestTime = System.currentTimeMillis();
long requestTicks = SystemClock.elapsedRealtime();
writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);
socket.send(request);
// read the response...
// ...
这里可以看到socket采用的是DatagramSocket(),也就是UDP协议的socket,所以确定同步时间用的是UDP协议
\frameworks\base\services\core\java\com\android\server\NetworkTimeUpdateService.java:
// Try again shortly
mTryAgainCounter++;
if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
resetAlarm(mPollingIntervalShorterMs);
} else {
// Try much later
mTryAgainCounter = 0;
resetAlarm(mPollingIntervalMs);
}
这一段就是设置同步失败后重新尝试的策略
刚开始同步会以config_ntpPollingIntervalShorter的5000毫秒的间隔重新尝试同步,在重试了config_ntpRetry定义的次数之后,会以config_ntpPollingInterval的时间间隔retry