转载请注明出处:http://blog.csdn.net/droyon/article/details/45701257
综述:Android网络时间更新,大体分两类。1、moderm相关更新,2、网络更新。本次主要介绍网路更新时间,主要涉及到NetworkTimeUpdateService,该类运行在SystemServer(ActivityManagerService)进程中。它有点特殊,从名字来看,其实Service,其实它和WifiService、ConnectivityManagerService等系统Service不同。
SystemServer.java
try { startBootstrapServices(); startCoreServices(); startOtherServices(); } catch (Throwable ex) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting system services", ex); throw ex; }
startOtherServices方法中,会初始化该类实例:
networkTimeUpdater = new NetworkTimeUpdateService(context);
在ActivityManagerService的systemReady方法中,初始化时间更新环境。
mActivityManagerService.systemReady(new Runnable() { @Override public void run() { try { if (networkManagementF != null) networkManagementF.systemReady(); } catch (Throwable e) { reportWtf("making Network Managment Service ready", e); } } }
涉及代码路径如下:
frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
frameworks/base/core/java/android/util/NtpTrustedTime.java
frameworks/base/core/java/android/net/SntpClient.java
一、NetworkTimeUpdateService实例
public NetworkTimeUpdateService(Context context) { mContext = context; mTime = NtpTrustedTime.getInstance(context); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); Intent pollIntent = new Intent(ACTION_POLL, null); mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);//时间同步有可能超时,使用该PendingIntent进行(间隔再次发起)时间同步。 mPollingIntervalMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpPollingInterval);//10天 mPollingIntervalShorterMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpPollingIntervalShorter);//30秒 mTryAgainTimesMax = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpRetry); mTimeErrorThresholdMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpThreshold); //LEUI-START [BUG][MOBILEP-6067] [System time sync added mDefaultServer = ((NtpTrustedTime) mTime).getServer(); mNtpServers.add(mDefaultServer); for (String str : SERVERLIST) { mNtpServers.add(str); } mTryAgainCounter = 0; //LEUI-END [BUG][MOBILEP-6067] [System time sync added }
在该构造上,有几个重要的变量:
1、mPollingIntervalMs:多次尝试同步时间无果,10天会再次发起时间同步请求
2、mPollingIntervalShorterMs :时间同步超时,再次发起时间同步请求。
3、SERVERLIST:时间同步服务器。此处建议多增加几个时间同步服务器,大陆、美国、台湾等多梯度配置。
4、初始化NtpTrustedTime对象。
mTime = NtpTrustedTime.getInstance(context);
一、NetworkTimeUpdateService初始化时间同步环境
开机后,会调用该类的systemRunning方法,在该方法中:
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); }
1、registerForTelephonyIntents该方法,注册监听来自Telephony Ril相关的广播。此部分会在moderm相关同步时间中介绍。
private void registerForTelephonyIntents() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME); intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE); mContext.registerReceiver(mNitzReceiver, intentFilter); }
2、registerForAlarms此方法,是配合第“一”中介绍的mPendingPollIntent 来工作的,主要作用是构造handler Message并再次发起时间同步请求。
3、registerForConnectivityIntents此方法监听移动数据连接,移动网络连接后,收到信息,发起时间同步请求。此部分会在moderm相关同步时间中介绍。
private void registerForConnectivityIntents() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); mContext.registerReceiver(mConnectivityReceiver, intentFilter); }
4、构建Message,发起时间同步请求。
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();
5、构建监听数据库的Observer,监听来自设置等发起的时间同步请求。在SettingsObserver中构建handler Message请求,发起时间同步。
mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED); mSettingsObserver.observe(mContext);
我们的第二部分,很多地方都会主动或者被动发送Handler Message请求,在我们Handler中,我们是如何处理的那?
三、时间同步请求处理逻辑。
在第二部分,我们讲到了接收的来自Telephony相关的广播,或者数据库变化,我们都会发送Message给Handler,我们的handler是如下处理这些请求的:
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_CONNECTED: onPollNetworkTime(msg.what); break; } } }
接收请求类型:EVENT_AUTO_TIME_CHANGED、EVENT_POLL_NETWORK_TIME、
EVENT_NETWORK_CONNECTED,这些请求逻辑,我们都会发起onPollNetworkTime来进行相关逻辑处理。
也就是说,onPollNetworkTime方法就是我们时间同步的主要关注对象。
1、onPollNetworkTime:
private void onPollNetworkTime(int event) { //1、是否勾选自动同步时间配置 // If Automatic time is not set, don't bother. if (!isAutomaticTimeRequested()) return; //2、mNitzTimeSetTime 来自Moderm,如果当前时间刚通过moderm更新不久,则不进行时间同步。 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; } //3、如果机器刚启动,或者机器运行时间大于mPollingIntervalMs,即10天,或者设置等发起的主动更新时间请求,则发起网络时间同步请求。否则,10天后再进行时间同步。 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"); //3.1、是否含有时间缓冲,如无,发起时间同步, // force refresh NTP cache when outdated if (mTime.getCacheAge() >= mPollingIntervalMs) { //LEUI-START [BUG][MOBILEP-6067] [System time sync added //mTime.forceRefresh(); int index = mTryAgainCounter % mNtpServers.size(); if (DBG) Log.d(TAG, "mTryAgainCounter = " + mTryAgainCounter + ";mNtpServers.size() = " + mNtpServers.size() + ";index = " + index + ";mNtpServers = " + mNtpServers.get(index)); //3.1.1、遍历时间服务器,发起时间同步 if (mTime instanceof NtpTrustedTime) { ((NtpTrustedTime) mTime).setServer(mNtpServers.get(index)); mTime.forceRefresh(); ((NtpTrustedTime) mTime).setServer(mDefaultServer); } else { mTime.forceRefresh(); } //LEUI-END [BUG][MOBILEP-6067] [System time sync added } //3.2、获取最新同步的时间缓冲数据,如无,则再次发起时间同步,间隔时间为mPollingIntervalShorterMs,即30秒。 // 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. //3.2.1、如果开机第一次同步或者最新时间与当前时间差别超过mTimeErrorThresholdMs即25秒,则进行时间设定。否则认定新同步时间与当前时间差别不大,不覆盖当前时间。 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 //3.2.2、设定同步时间 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 //3.3 如果不大于最大同步次数,30秒后进行时间同步,否则,10天后更新。 mTryAgainCounter++; if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) { resetAlarm(mPollingIntervalShorterMs); } else { // Try much later mTryAgainCounter = 0; resetAlarm(mPollingIntervalMs); } return; } } //4、如果刚更新时间不久,则10天后再发起时间同步请求。 resetAlarm(mPollingIntervalMs); }
四、三中介绍了时间获取的相关逻辑,我们接下来介绍下时间是如何发起同步的,这个方法的主角为:NtpTrustedTime
在该类中通过forceRefresh方法来更新获取服务器时间。
public boolean forceRefresh() { if (mServer == null) { // missing server, so no trusted time available 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来封装请求。
SntpClient.java
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 DatagramPacket response = new DatagramPacket(buffer, buffer.length); socket.receive(response); long responseTicks = SystemClock.elapsedRealtime(); long responseTime = requestTime + (responseTicks - requestTicks); // extract the results long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET); long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET); long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET); 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 (false) Log.d(TAG, "round trip: " + roundTripTime + " ms"); // if (false) Log.d(TAG, "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 (false) Log.d(TAG, "request time failed: " + e); return false; } finally { if (socket != null) { socket.close(); } } return true; }
我们传入在NetworkTimeUpdateService传入的服务器地址以及请求超时时间,向host服务器发起请求,并将相应结果按照编解码规则封装进二进制数组。
总结:NetworkTimeUpdateService时间同步,一旦发起成功的时间同步,时间数据会存在内存中,并根据当前机器运行时间来设定最新的时间。