Platform: Rockchip
OS: Android 6.0
Kernel: 3.10.92
Android从外部同步时间的方式有两种
1. 从运营商获取,也就是插SIM卡的方式获取,运营商提供时间和时区,用的是NITZ协议
2. 网络获取时间,也就是WIFI或GPS的方式获取,网络只能提供时间,用的是SNTP协议(NTP协议的精简版)
这里关注在连接Wifi的情况下,开启自动获取时间,这时会调用:
onSharedPreferenceChanged -> DateTimeSettings.java //在initUI()初始化监听
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (key.equals(KEY_AUTO_TIME)) {
boolean autoEnabled = preferences.getBoolean(key, true);
//设置AUTO_TIME为enable到ContentProvider中
Settings.Global.putInt(getContentResolver(), Settings.Global.AUTO_TIME,
autoEnabled ? 1 : 0);
mTimePref.setEnabled(!autoEnabled);
mDatePref.setEnabled(!autoEnabled);
} else if (key.equals(KEY_AUTO_TIME_ZONE)) {
boolean autoZoneEnabled = preferences.getBoolean(key, true);
Settings.Global.putInt(
getContentResolver(), Settings.Global.AUTO_TIME_ZONE, autoZoneEnabled ? 1 : 0);
mTimeZone.setEnabled(!autoZoneEnabled);
}
}
另一方面,SettingsObserver监听Settings.Global.AUTO_TIME的变化,使用的是observer机制,机制可查看后面参考
private static class SettingsObserver extends ContentObserver {
void observe(Context context) {
ContentResolver resolver = context.getContentResolver();
resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
false, this);
}
}
当Settings.Global.AUTO_TIME变化时,SettingsObserver收到EVENT_AUTO_TIME_CHANGED事件被触发
private static class SettingsObserver extends ContentObserver {
@Override
public void onChange(boolean selfChange) {
//发给handler处理
mHandler.obtainMessage(mMsg).sendToTarget();
}
}
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;
}
}
onPollNetworkTime -> onPollNetworkTimeUnderWakeLock
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.
//如果已经从运行商(有SIM卡的情况)那边获取时间而且是在mPollingIntervalMs时间内设置过,那么就不设置NTP了
//mPollingIntervalMs在framework/base/core/res/res/values/config.xml配置,为864000000,也就是10天
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
//如果缓存超过了10天,就重新获取,mTime即NtpTrustedTime类
if (mTime.getCacheAge() >= mPollingIntervalMs) {
mTime.forceRefresh();
}
// only update when NTP time is fresh
//NTP时间刷新后更新本地时间,
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.
//当NTP与本地时间的差比mTimeErrorThresholdMs大时才能更新时间
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);
}
NtpTrustedTime.java:
public boolean forceRefresh() {
//sntp协议
final SntpClient client = new SntpClient();
//请求时间
if (client.requestTime(mServer, (int) mTimeout)) {
mHasCache = true;
mCachedNtpTime = client.getNtpTime();
//更新后会改变getCacheAge()的值,上面设置到本地时间会判断
mCachedNtpElapsedRealtime = client.getNtpTimeReference();
mCachedNtpCertainty = client.getRoundTripTime() / 2;
return true;
} else {
return false;
}
}
mServer为 res.getString(com.android.internal.R.string.config_ntpServer)
对应的配置文件在./core/res/res/values/config.xml
也就是说时间是从这个远程的server获取的。
参考:
Android 时间同步原理分析
Android中内容观察者的使用—- ContentObserver类详解
Android 7.1.1时间更新NITZ和NTP详解