赞
踩
NTP(Network Time Protocol)提供准确时间,首先要有准确的时间来源,这一时间应该是国际标准时间UTC。 NTP获得UTC的时间来源可以是原子钟、天文台、卫星,也可以从Internet上获取。这样就有了准确而可靠的时间源。时间按NTP服务器的等级传播。与NITZ不同的是,NTP需要从专门的NTP服务器来获取时间,只要手机连接上网络,都可以实现时间的更新。
//frameworks/base/services/java/com/android/server/SystemServer.java //是否为手表设备 boolean isWatch = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WATCH); //通过属性控制是否需要创建NTP服务 boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime", false); ... if (!isWatch && !disableNetworkTime) { traceBeginAndSlog("StartNetworkTimeUpdateService"); try { if (useNewTimeServices) { networkTimeUpdater = new NewNetworkTimeUpdateService(context); } else { networkTimeUpdater = new OldNetworkTimeUpdateService(context); } Slog.d(TAG, "Using networkTimeUpdater class=" + networkTimeUpdater.getClass()); ServiceManager.addService("network_time_update_service", networkTimeUpdater); } catch (Throwable e) { reportWtf("starting NetworkTimeUpdate service", e); } traceEnd(); }
从上面代码分析,如果是
watch
设备,则不创建NTP服务,其次,如果配置系统属性config.disable_networktime
属性为true
,同样不创建NTP服务。手机设备一般情况下是通过NewNetworkTimeUpdateService
创建NTP服务。下面着重分析NewNetworkTimeUpdateService
//frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
// Normal polling frequency
//正常请求间隔
private final long mPollingIntervalMs;
// Try-again polling interval, in case the network request failed
//请求失败重新请求间隔
private final long mPollingIntervalShorterMs;
// Number of times to try again
//请求失败重试次数
private final int mTryAgainTimesMax;
// If the time difference is greater than this threshold, then update the time.
//如果时间误差大于mTimeErrorThresholdMs,忽略缓存时间强制更新时间
private final int mTimeErrorThresholdMs;
//NTP时间同步实现类
private final NtpTrustedTime mTime;
2、属性初始化
//frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java ## NetworkTimeUpdateService()
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);
frameworks/base/core/java/android/util/NtpTrustedTime.java public static synchronized NtpTrustedTime getInstance(Context context) { if (sSingleton == null) { final Resources res = context.getResources(); final ContentResolver resolver = context.getContentResolver(); //NTP服务器地址 final String defaultServer = res.getString( com.android.internal.R.string.config_ntpServer); final long defaultTimeout = res.getInteger( com.android.internal.R.integer.config_ntpTimeout); //Settings.Global.NTP_SERVER默认为null,可以通过更改xml文件或者指令更改NTP服务器 final String secureServer = Settings.Global.getString( resolver, Settings.Global.NTP_SERVER); //Settings.Global.NTP_TIMEOUT默认为null,可以通过更改xml文件或者指令更改请求超时时间 final long timeout = Settings.Global.getLong( resolver, Settings.Global.NTP_TIMEOUT, defaultTimeout); final String server = secureServer != null ? secureServer : defaultServer; sSingleton = new NtpTrustedTime(server, timeout); sContext = context; } return sSingleton; });
3、更改属性配置,自定义NTP请求参数
//frameworks/base/core/res/res/values/config.xml
<!-- Remote server that can provide NTP responses. -->
<string translatable="false" name="config_ntpServer">time.android.com</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 in milliseconds. -->
<integer name="config_ntpTimeout">5000</integer>
//frameworks/base/packages/SettingsProvider/res/values/defaults.xml 增加/修改ntp_server value值
<string name="ntp_server" translatable="false">cn.pool.ntp.org</string>
//将自定义ntp服务器插入到数据库
//frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
private void loadSecureSettings(SQLiteDatabase db) {
......
//add by lrh
loadStringSetting(stmt, Settings.Secure.NTP_SERVER,R.string.ntp_server);
//end
}
//更改请求超时时间与上面方法相同,不再赘述!
settings [--user NUM] get namespace key
settings [--user NUM] put namespace key value
(namespace is one of {system, secure, global})
//串口输入(也可以用adb方式)
settings put global ntp_server ntp.test1.com
查看数据库
4、NTP请求与缓存
//frameworks/base/core/java/android/util/NtpTrustedTime.java public boolean forceRefresh(Network network) { //检查ntp服务器是否为null 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 = 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; } //借助SntpClient进行NTP请求 if (LOGD) Log.d(TAG, "forceRefresh() from cache miss"); final SntpClient client = new SntpClient(); if (client.requestTime(mServer, (int) mTimeout, network)) { //是否有缓存 mHasCache = true; // mCachedNtpTime = client.getNtpTime(); mCachedNtpElapsedRealtime = client.getNtpTimeReference(); mCachedNtpCertainty = client.getRoundTripTime() / 2; return true; } else { return false; } }
NTP请求(请求端口为123
,通讯方式为TCP
通信)
public boolean requestTime(String host, int timeout, Network network) { // This flag only affects DNS resolution and not other socket semantics, // therefore it's safe to set unilaterally rather than take more // defensive measures like making a copy. network.setPrivateDnsBypass(true); InetAddress address = null; try { address = network.getByName(host); } catch (Exception e) { EventLogTags.writeNtpFailure(host, e.toString()); if (DBG) Log.d(TAG, "request time failed: " + e); return false; } //NTP_PORT = 123 return requestTime(address, NTP_PORT, timeout, network); } public boolean requestTime(InetAddress address, int port, int timeout, Network network) { DatagramSocket socket = null; final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NTP); try { socket = new DatagramSocket(); network.bindSocket(socket); socket.setSoTimeout(timeout); byte[] buffer = new byte[NTP_PACKET_SIZE]; DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, 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 final long requestTime = System.currentTimeMillis(); final long requestTicks = SystemClock.elapsedRealtime(); writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime); socket.send(request); // read the response DatagramPacket response = new DatagramPacket(buffer, buffer.length); socket.receive(response); final long responseTicks = SystemClock.elapsedRealtime(); final long responseTime = requestTime + (responseTicks - requestTicks); // extract the results final byte leap = (byte) ((buffer[0] >> 6) & 0x3); final byte mode = (byte) (buffer[0] & 0x7); final int stratum = (int) (buffer[1] & 0xff); final long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET); final long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET); final long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET); /* do sanity check according to RFC */ // TODO: validate originateTime == requestTime. checkValidServerReply(leap, mode, stratum, transmitTime); long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime); // receiveTime = originateTime + transit + skew // responseTime = transmitTime + transit - skew // clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2 // = ((originateTime + transit + skew - originateTime) + // (transmitTime - (transmitTime + transit - skew)))/2 // = ((transit + skew) + (transmitTime - transmitTime - transit + skew))/2 // = (transit + skew - transit + skew)/2 // = (2 * skew)/2 = skew long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2; EventLogTags.writeNtpSuccess(address.toString(), roundTripTime, clockOffset); if (DBG) { Log.d(TAG, "round trip: " + roundTripTime + "ms, " + "clock offset: " + clockOffset + "ms"); } // save our results - use the times on this side of the network latency // (response rather than request time) mNtpTime = responseTime + clockOffset; mNtpTimeReference = responseTicks; mRoundTripTime = roundTripTime; } catch (Exception e) { EventLogTags.writeNtpFailure(address.toString(), e.toString()); if (DBG) Log.d(TAG, "request time failed: " + e); return false; } finally { if (socket != null) { socket.close(); } TrafficStats.setThreadStatsTag(oldTag); } return true; }
以上涉及到的时间转换与计算请结合代码与协议进行分析
至此,NTP更新服务分析完成…
如有错误,敬请指正~~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。