赞
踩
两种时间更新机制
NITZ
NITZ(Network Identity and Time Zone,网络标识和时区),是一种用于自动配置本地的时间和日期的机制,同时也通过无线网向移动设备提供运营商信息。NITZ是自从PHASE 2+ RELEASE 96 的GSM中的可选功能,经常被用来自动更新移动电话的系统时钟。NITZ需要运营商网络支持(通过CS网络),目前国内电信、移动都支持NITZ方式更新时间日期,而联通目前不支持
NTP
NTP:NTP(Network Time Protocol)提供准确时间,首先要有准确的时间来源,这一时间应该是国际标准时间UTC。 NTP获得UTC的时间来源可以是原子钟、天文台、卫星,也可以从Internet上获取。这样就有了准确而可靠的时间源。时间按NTP服务器的等级传播。与NITZ不同的是,NTP需要从专门的NTP服务器来获取时间,只要手机连接上网络,都可以实现时间的更新。
一、Android网络时间更新
Android网络时间更新,大体分两类。1、moderm相关更新,2、网络更新。本次主要介绍网路更新时间,主要涉及到NetworkTimeUpdateService,该类运行在SystemServer(ActivityManagerService)进程中。它有点特殊,从名字来看,其实Service,其实它和WifiService、ConnectivityManagerService等系统Service不同。
ZygoteInit.java的startSystemServer函数中启动了SyetemServer进程,代码路径如下所示:\frameworks\base\services\java\com\android\server\SystemServer.java
/**
* The main entry point from zygote.
*/
public static void main(String[] args) {
new SystemServer().run();
}
startOtherServices方法中,会初始化该类实例:
if (!disableNetwork && !disableNetworkTime) {
traceBeginAndSlog("StartNetworkTimeUpdateService");
try {
networkTimeUpdater = new NetworkTimeUpdateService(context);
ServiceManager.addService("network_time_update_service", networkTimeUpdater);
} catch (Throwable e) {
reportWtf("starting NetworkTimeUpdate service", e);
}
traceEnd();
}
网络更新的相关代码:
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) { Log.d(TAG, "NetworkTimeUpdateService()"); mContext = context; mTime = NtpTrustedTime.getInstance(context); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); //时间同步有可能超时,使用该PendingIntent进行(间隔再次发起)时间同步。 Intent pollIntent = new Intent(ACTION_POLL, null); mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0); //正常轮询频率 24小时 mPollingIntervalMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpPollingInterval); //再次尝试轮询间隔,以防网络请求失败 60秒 mPollingIntervalShorterMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpPollingIntervalShorter); //重试次数 3次 mTryAgainTimesMax = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpRetry); //如果时间差大于此阈值,则更新时间。 5秒 mTimeErrorThresholdMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpThreshold); mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, TAG); Log.d(TAG, "mPollingIntervalMs "+mPollingIntervalMs); Log.d(TAG, "mPollingIntervalShorterMs "+mPollingIntervalShorterMs); Log.d(TAG, "mTryAgainTimesMax "+mTryAgainTimesMax); Log.d(TAG, "mTimeErrorThresholdMs "+mTimeErrorThresholdMs); }
在SystemServer.java中调用systemRunning(),初始化接收方并启动第一个NTP请求
//在SystemServer.java中调用systemRunning()
traceBeginAndSlog("MakeCountryDetectionServiceReady");
try {
if (countryDetectorF != null) countryDetectorF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying CountryDetectorService running", e);
}
traceEnd();
traceBeginAndSlog("MakeNetworkTimeUpdateReady");
NetworkTimeUpdateService初始化时间同步环境开机后,会调用该类的systemRunning方法,在该方法中中实现systemRunning();
public void systemRunning() {
registerForTelephonyIntents();
registerForAlarms();
HandlerThread thread = new HandlerThread(TAG);
thread.start();
mHandler = new MyHandler(thread.getLooper());
mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
mSettingsObserver.observe(mContext);
}
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);
}
系统启动后,会收到一个Telephony的广播android.intent.action.NETWORK_SET_TIMEZONE,获取系统深度睡眠时间
//尼兹时间事件的接收者
private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (DBG) Log.d(TAG, "Received " + action);
if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
mNitzTimeSetTime = SystemClock.elapsedRealtime();//从设备开机到现在的时间,单位毫秒,含系统深度睡眠时间
} else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
mNitzZoneSetTime = SystemClock.elapsedRealtime();
}
}
};
当我们点击设置中的自动确定日期和时间时,然后通过观察者模式会执行如下代码,构建监听数据库的Observer,监听来自设置等发起的时间同步请求。在SettingsObserver中构建handler Message请求,发起时间同步。
源码路径:packages/apps/Settings/src/com/android/settings/DateTimeSettings.java
mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED); mSettingsObserver.observe(mContext); private static class SettingsObserver extends ContentObserver { private int mMsg; private Handler mHandler; SettingsObserver(Handler handler, int msg) { super(handler); mHandler = handler; mMsg = msg; } void observe(Context context) { ContentResolver resolver = context.getContentResolver(); resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME), false, this); } @Override public void onChange(boolean selfChange) { Log.d(TAG, "onChange() selfChange "+selfChange); mHandler.obtainMessage(mMsg).sendToTarget(); } }
接收的来自Telephony相关的广播,或者数据库变化,我们都会发送Message给Handler,我们的handler是如下处理这些请求的:
/** Handler to do the network accesses on */ 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; } } }
当我们打开移动数据或者wifi打开连接网络后是就会执行如下代码
private class NetworkTimeUpdateCallback extends NetworkCallback {
@Override
public void onAvailable(Network network) {
Log.d(TAG, String.format("New default network %s; checking time.", network));
mDefaultNetwork = network;
// Running on mHandler so invoke directly.
onPollNetworkTime(EVENT_NETWORK_CHANGED);
}
@Override
public void onLost(Network network) {
if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
}
}
下面是时间同步请求处理逻辑
接收请求类型:EVENT_AUTO_TIME_CHANGED、EVENT_POLL_NETWORK_TIME、
EVENT_NETWORK_CONNECTED,这些请求逻辑,我们都会发起onPollNetworkTime来进行相关逻辑处理。也就是说,onPollNetworkTime方法就是我们时间同步的主要关注对象。
private void onPollNetworkTime(int event) { // If Automatic time is not set, don't bother. Similarly, if we don't // have any default network, don't bother. Log.d(TAG, "isAutomaticTimeRequested() "+isAutomaticTimeRequested()); Log.d(TAG, "mDefaultNetwork() "+mDefaultNetwork); //自动使用网络提供时间sAutomaticTimeRequested() 就是true if (!isAutomaticTimeRequested() || mDefaultNetwork == null) return; mWakeLock.acquire(); try { onPollNetworkTimeUnderWakeLock(event); } finally { mWakeLock.release(); } } 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); }
NTPServer服务器配置
修改配置位置:frameworks\base\core\res\res\values\config.xml
<string translatable="false" name="config_ntpServer">2.android.pool.ntp.org</string>
NTP从服务器获取时间分析
首先定义一个接口类TrustedTime,里面有如下方法:
public interface TrustedTime { /** * Force update with an external trusted time source, returning {@code true} * when successful. */ public boolean forceRefresh(); /** * Check if this instance has cached a response from a trusted time source. */ public boolean hasCache(); /** * Return time since last trusted time source contact, or * {@link Long#MAX_VALUE} if never contacted. */ public long getCacheAge(); /** * Return certainty of cached trusted time in milliseconds, or * {@link Long#MAX_VALUE} if never contacted. Smaller values are more * precise. */ public long getCacheCertainty(); /** * Return current time similar to {@link System#currentTimeMillis()}, * possibly using a cached authoritative time source. */ public long currentTimeMillis(); }
NtpTrustedTime类实现TrustedTime的方法,在forceRefresh()中获取服务器的时间。
@Override 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(); final SprdSntpClient client = new SprdSntpClient(); if (client.requestTime(mServer, (int) mTimeout)) { mHasCache = true; mCachedNtpTime = client.getNtpTime(); mCachedNtpElapsedRealtime = client.getNtpTimeReference(); mCachedNtpCertainty = client.getRoundTripTime() / 2; return true; } else { return false; } }
日志流程
05-29 11:14:38.284 901 901 D NetworkTimeUpdateService: NetworkTimeUpdateService()
05-29 11:14:38.285 901 901 D NetworkTimeUpdateService: mPollingIntervalMs 86400000
05-29 11:14:38.285 901 901 D NetworkTimeUpdateService: mPollingIntervalShorterMs 60000
05-29 11:14:38.285 901 901 D NetworkTimeUpdateService: mTryAgainTimesMax 3
05-29 11:14:38.285 901 901 D NetworkTimeUpdateService: mTimeErrorThresholdMs 5000
05-29 11:14:44.805 901 901 D NetworkTimeUpdateService: Received android.intent.action.NETWORK_SET_TIMEZONE
05-29 11:20:12.155 901 1303 D NetworkTimeUpdateService: New default network 100; checking time.
05-29 11:20:12.158 901 1303 D NetworkTimeUpdateService: System time = 1590722412158
05-29 11:20:12.158 901 1303 D NetworkTimeUpdateService: Before Ntp fetch
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。