赞
踩
相关时序图如下:
由于NITZ主要依赖于运营商,但在国内移动和联通貌似不怎么好用,在这里就不在详细说了,简单总结下如下:
1、在ServiceStateTracker构造方法里调用setOnNITZTime注册RIL事件RIL_UNSOL_NITZ_TIME_RECEIVED
2、RIL层上报RIL_UNSOL_NITZ_TIME_RECEIVED,在ServiceStateTracker的handleMessage里处理
3、调用ServiceStateTracker的setTimeFromNITZString设置时间和时区,在setAndBroadcastNetworkSetTime里调用setCurrentTimeMillis设置系统时间,并发送广播通知NetworkTimeUpdateService
当SystemServer启动,会调用networkTimeUpdaterF.systemRunning()初始化各种NTP request监听
- public void systemRunning() {
- registerForTelephonyIntents();
- registerForAlarms();
- registerForConnectivityIntents();
-
- HandlerThread thread = new HandlerThread(TAG);
- thread.start();
- mHandler = new MyHandler(thread.getLooper());
- // Check the network time on the new thread
- mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
-
- mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
- mSettingsObserver.observe(mContext);
- }
在registerForTelephonyIntents中主要是监听ACTION_NETWORK_SET_TIME和ACTION_NETWORK_SET_TIMEZONE的广播,registerForAlarms中监听"com.android.server.NetworkTimeUpdateService.action.POLL"广播,registerForConnectivityIntents监听网络状态改变的广播,SettingsObserver里监听Settings.Global.AUTO_TIME值的改变
- //frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
- 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;
- }
- }
- }
在这个MyHandler中可以看到,当上面某种监听触发时都会调用onPollNetworkTime,而这个方法里主要调用了onPollNetworkTimeUnderWakeLock,接下来看看这个方法
- //frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
- 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) {
- 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) {
- 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
- || 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);
- }
- } 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);
- }
- return;
- }
- }
- resetAlarm(mPollingIntervalMs);
- }
这个方法中主要调用了TrustedTime实例的forceRefresh方法去获取时间,获取之后通过mTime.currentTimeMillis获得获取成功之后的时间ntp,最后调用 SystemClock.setCurrentTimeMillis(ntp)设置系统时间有几个参数需要特别说下
- public NetworkTimeUpdateService(Context context) {
- mContext = context;
- mTime = NtpTrustedTime.getInstance(context);
- ...
- //正常的轮询频率
- 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);
- ...
- }
对应参数的配置如下,可能存在overlay替换,此处是framework里默认配置的值
- //frameworks/base/core/res/res/values/config.xml
- <!-- 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>
上面讲到主要通过TrustedTime实例的forceRefresh获取时间,下面就来跟下这个方法
- //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;
- }
- }
上面方法中主要是通过SntpClient的requestTime根据传入的mServer获取时间,mServer是在调用NtpTrustedTime的getInstance中初始化的,具体如下
- public static synchronized NtpTrustedTime getInstance(Context context) {
- if (sSingleton == null) {
- final Resources res = context.getResources();
- final ContentResolver resolver = context.getContentResolver();
-
- final String defaultServer = res.getString(
- com.android.internal.R.string.config_ntpServer);
- final long defaultTimeout = res.getInteger(
- com.android.internal.R.integer.config_ntpTimeout);
-
- final String secureServer = Settings.Global.getString(
- resolver, Settings.Global.NTP_SERVER);
- 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;
- }
config_ntpServer和config_ntpTimeout也是在framework下res中配置的,Settings.Global.NTP_SERVER和Settings.Global.NTP_TIMEOUT是配置在SettingProvider中的,就不在具体说明了。从上面代码可看出server是由secureServer和defaultServer决定的
- //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>
- <!-- Timeout to wait for NTP server response in milliseconds. -->
- <integer name="config_ntpTimeout">5000</integer>
其中SntpClient主要是提供访问Ntp server的一个类,在requestTime中主要通过DatagramSocket访问传入的server,来获取时间,具体实现如下:
- //frameworks/base/core/java/android/net/SntpClient.java
- public boolean requestTime(InetAddress address, int port, int timeout) {
- DatagramSocket socket = null;
- try {
- socket = new DatagramSocket();
- 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;
- 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) {
- if (DBG) Log.d(TAG, "request time failed: " + e);
- return false;
- } finally {
- if (socket != null) {
- socket.close();
- }
- }
- return true;
- }
其实Ntp说的明白点,就是通过网络去获取时间然后更新系统时间,具体流程上面也简单说了下,当遇到手机不能更新时间时,先要看看网络是否可用,网络可用的情况下,就得看看对应的NtpServer是否能够访问。曾经就遇到过移动的数据业务不能访问NtpServer,导致手机不能更新时间。一般可以通过ntp等关键字在log中搜索,一般是SocketException或unknown host的错误。也可以直接adb shell命令进去手机,然后利用ping NtpServer 查看当前服务器是否可访问。在这里需要说的一点是,NtpServer一般都是对应的网址,访问网络时会根据当前的运营商网络,找到对应的IP地址,再去访问,有可能存在同一个NtpServer联通网络可以访问而移动网络不行的情况。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。