Android设备时间同步主要通过两种方式,非移动设备这里只讨论NTP
-
NITZ:Network Identity and Time Zone(网络标识和时区),是一种用于自动配置本地的时间和日期的机制,需要运营商支持,可从运营商获取时间和时区具体信息。
-
NTP:Network Time Protocol(网络时间协议),用来同步网络中各个计算机的时间的协议。在手机中,NTP更新时间的方式是通过GPRS或wifi向特定服务器获取时间信息(不包含时区信息)
涉及的主要服务
frameworks/base/services/core/java/com/android/server/NewNetworkTimeUpdateService.java
frameworks/base/core/java/android/util/NtpTrustedTime.java
frameworks/base/services/core/java/com/android/server/AlarmManagerService.java
frameworks/base/core/java/android/net/SntpClient.java
frameworks/base/core/res/res/values/config.xml
config文件要到考虑overlay,对应下面代码的各项默认值
<string translatable="false" name="config_ntpServer">ntp.aliyun.com</string>
<integer name="config_ntpPollingInterval">86400000</integer>
<integer name="config_ntpPollingIntervalShorter">8000</integer>
<integer name="config_ntpRetry">3</integer>
<integer name="config_ntpThreshold">5000</integer>
<integer name="config_ntpTimeout">5000</integer>
日志分析
06-28 05:40:21.423 2041 2041 I SystemServer: StartAlarmManagerService
06-28 05:40:21.427 2041 2041 D AlarmManagerService: Kernel timezone updated to -480 minutes west of GMT
06-28 05:40:21.427 2041 2041 I AlarmManager: Current time only 125427, advancing to build time 1687902002000 //设置为rom编译时间
06-28 05:40:21.427 2041 2041 I AlarmManager: xxx_time_sync setKernelTime millis = 1687902002000
06-28 05:40:21.427 2041 2041 D AlarmManagerService: Setting time of day to sec=1687902002 //jni调用 AlarmManagerService.cpp
06-28 05:40:21.821 2041 2041 I SystemServer: StartNetworkTimeUpdateService
06-28 05:40:21.822 2041 2041 I NetworkTimeUpdateService: xxx_time_sync mPollingIntervalMs = 86400000 mPollingIntervalShorterMs = 8000 mTryAgainTimesMax = 3 mTimeErrorThresholdMs=5000
06-28 05:40:21.822 2041 2041 D SystemServer: Using networkTimeUpdater class=class com.android.server.NewNetworkTimeUpdateService //用NewNetworkTimeUpdateService
06-28 05:40:21.823 2041 2041 D SystemServerTiming: StartNetworkTimeUpdateService took to complete: 2ms
06-28 05:41:48.944 2041 2606 I NetworkTimeUpdateService: New default network 100; checking time. //联网
06-28 05:41:48.945 2041 2606 I NetworkTimeUpdateService: Stale NTP fix; forcing refresh
06-28 10:11:24.014 2041 2606 D SntpClient: round trip: 36ms, clock offset: 16175362ms //sntp request请求时间
06-28 10:11:24.014 2041 2606 I NtpTrustedTime: xxx_time_sync SntpClient requestTime mCachedNtpTime = 1687918284376
06-28 10:11:24.015 2041 2606 I NtpTrustedTime: currentTimeMillis() cache hit
06-28 10:11:24.016 2041 2606 I NtpTrustedTime: currentTimeMillis() cache hit
06-28 10:11:24.017 2041 2606 I NtpTrustedTime: currentTimeMillis() cache hit
06-28 10:11:24.017 2041 2606 I NetworkTimeUpdateService: xxx_time_sync updateSystemClock currentTimeMillis = 1687918284378 forceUpdate =false
06-28 10:11:24.019 2041 2606 I SystemClock: xxx_time_sync setCurrentTimeMillis millis = 1687918284379
06-28 10:11:24.020 2041 2606 I AlarmManager: xxx_time_sync setTimeImpl millis = 1687918284379 currentTimeMillis =1687902109019
06-28 10:11:24.020 2041 2606 I AlarmManager: xxx_time_sync setKernelTime millis = 1687918284379
从日志可以看到AlarmManagerService先被启动。在AlarmManagerService onStart()会有一段check时间的逻辑。即选取使用当前时间、根文件系统的时间戳和ro.build.date.utc系统属性的值(以秒为单位)中的最近时间。
// Ensure that we're booting with a halfway sensible current time. Use the
// most recent of Build.TIME, the root file system's timestamp, and the
// value of the ro.build.date.utc system property (which is in seconds).
final long systemBuildTime = Long.max(
1000L * SystemProperties.getLong("ro.build.date.utc", -1L),
Long.max(Environment.getRootDirectory().lastModified(), Build.TIME));
if (mInjector.getCurrentTimeMillis() < systemBuildTime) {
Slog.i(TAG, "Current time only " + mInjector.getCurrentTimeMillis()
+ ", advancing to build time " + systemBuildTime);
mInjector.setKernelTime(systemBuildTime);
}
上面部分代码对应以下log。前者时间为1970年,后者为ro.build.date.utc的时间。
06-28 05:40:21.427 2041 2041 I AlarmManager: Current time only 125427, advancing to build time 1687902002000
NewNetworkTimeUpdateService Android10版本
NetworkTimeUpdateService是用于处理ntp同步的服务。在Android10版本,有NewNetworkTimeUpdateService/OldNetworkTimeUpdateService两个服务分别实现NetworkTimeUpdateService。从SystemServer中可以判断真正使用的是NewNetworkTimeUpdateService。
继续看代码主要分析 构造方法以及/systemRunning()
public NewNetworkTimeUpdateService(Context context) {
mContext = context;
mTime = NtpTrustedTime.getInstance(context);
mAlarmManager = mContext.getSystemService(AlarmManager.class);
mCM = mContext.getSystemService(ConnectivityManager.class);
Intent pollIntent = new Intent(ACTION_POLL, null);
mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
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);
mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, TAG);
}
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);
}
主要分析
- registerForAlarms();
- mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
- mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
ACTION_POLL用于绑定mPendingPollIntent最终注册在AlarmManagerService中,定时校验时间。 对应的event,EVENT_POLL_NETWORK_TIME。
private static final String ACTION_POLL =
"com.android.server.NetworkTimeUpdateService.action.POLL";
private void registerForAlarms() {
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
}
}, new IntentFilter(ACTION_POLL));
}
mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler) 通过ConnectivityManager注册网络回调,如果是available,则触发event,EVENT_NETWORK_CHANGED。
@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;
}
}
mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
对应设置中自动时间同步的swtich开关
void observe(Context context) {
ContentResolver resolver = context.getContentResolver();
resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
false, this);
}
onPollNetworkTime处理EVENT
只有在网络变化、自动更新时间设置变化、定时任务三种情况才会触发时间同步。(不一定同步成功。)
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;
}
}
}
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.
if (mDefaultNetwork == null) return; //没有网络就不校正
mWakeLock.acquire();
try {
onPollNetworkTimeUnderWakeLock(event);
} finally {
mWakeLock.release();
}
}
重点分析onPollNetworkTimeUnderWakeLock
- mTime即NtpTrustedTime
- mPollingIntervalMs 即 config_ntpPollingInterval 默认 86400000 = 24小时
- mTryAgainTimesMax config_ntpRetry 默认3次
- mPollingIntervalShorterMs config_ntpPollingIntervalShorter 默认60s
private void onPollNetworkTimeUnderWakeLock(int event) {
// Force an NTP fix when outdated
//同步时间超过24小时则强制刷新
if (mTime.getCacheAge() >= mPollingIntervalMs) {//24小时
if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
mTime.forceRefresh();
}
//小于24小时 更新24小时
if (mTime.getCacheAge() < mPollingIntervalMs) {
// Obtained fresh fix; schedule next normal update
resetAlarm(mPollingIntervalMs); //重新计时
if (isAutomaticTimeRequested()) {//Settings.Global.AUTO_TIME 开关是否打开
updateSystemClock(event);
}
} else {
// No fresh fix; schedule retry
mTryAgainCounter++;
if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
resetAlarm(mPollingIntervalShorterMs); //60s 短定时任务
} else {
// Try much later
mTryAgainCounter = 0;
resetAlarm(mPollingIntervalMs);
}
}
}
getCacheAge()如何计算
- mHasCache forceRefresh才为true ,默认是false。
- elapsedRealtime开机启动开始计算的时间 。
- mCachedNtpElapsedRealtime forceRefresh的时候 通过SntpClient获取的时间。
所以getCacheAge()理解成为距离上次强制更新时间的间隔
public long getCacheAge() {
if (mHasCache) {
return SystemClock.elapsedRealtime() - mCachedNtpElapsedRealtime;
} else {
return Long.MAX_VALUE;
}
}
forceRefresh()
二次检查connectivity状态,这里代码省略。
- mServer 这里是config文件中对应的ntpserver地址,我们是aliyun
- mTimeout这里的timeout是上边传下来的。
- 如果ntpserver正常 网络连接正常通过stnpClient requestTime更新时间,具体通过socket
frameworks/base/core/java/android/util/NtpTrustedTime.java
public boolean forceRefresh(Network network) {
if (TextUtils.isEmpty(mServer)) { //ntpserver为空 不更新了
// 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;
}
if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
final SntpClient client = new SntpClient(); //stpnClient 请求更新
if (client.requestTime(mServer, (int) mTimeout, network)) {
mHasCache = true;
mCachedNtpTime = client.getNtpTime();//刷新ntp时间
mCachedNtpElapsedRealtime = client.getNtpTimeReference();
mCachedNtpCertainty = client.getRoundTripTime() / 2;
return true;
} else {
return false;
}
}
updateSystemClock
SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis()) 最终通过alramManager setTime()更新时间
private void updateSystemClock(int event) {
final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
if (!forceUpdate) {
if (getNitzAge() < mPollingIntervalMs) {
if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
return;
}
final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
if (skew < mTimeErrorThresholdMs) {
if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
return;
}
}
SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
}
以上为Android10更新逻辑
Android11 12的改变
11 12的版本,只有一个NetworkTimeUpdateService。其中最大的差别是在onPollNetworkTimeUnderWakeLock()
依旧是对比mPollingIntervalMs,超过(默认24小时强制刷新) 更新时间则通过mTimeDetector.suggestNetworkTime(timeSuggestion);等等一系列复杂的逻辑最终还是调用AlarmManager设置时间。 可以读下代码,更复杂更科学。因为和我在追的问题无关,所以不深入了。
private void onPollNetworkTimeUnderWakeLock(int event) {
// Force an NTP fix when outdated
NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {// 超过24小时刷新
if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
mTime.forceRefresh();
cachedNtpResult = mTime.getCachedTimeResult();
}
if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
// Obtained fresh fix; schedule next normal update
resetAlarm(mPollingIntervalMs);
// Suggest the time to the time detector. It may choose use it to set the system clock.
TimestampedValue<Long> timeSignal = new TimestampedValue<>(
cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis());
NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateService. event=" + event);
mTimeDetector.suggestNetworkTime(timeSuggestion);
} else {
// No fresh fix; schedule retry
mTryAgainCounter++;
if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
resetAlarm(mPollingIntervalShorterMs);
} else {
// Try much later
mTryAgainCounter = 0;
resetAlarm(mPollingIntervalMs);
}
}
}
总结
Android设备默认时间首先通过AlarmManagerService根据buildtime,根目录文件修改时间,当前时间选一个最接近的设为默认时间。然后根据ntp策略进行时间同步。
对于非移动Android设备来说,只有在三种情况下会触发ntp同步。
- 网络变化avaiable的时候
- 自动时间同步switch改变的时候
- 定时任务启动的时候 一般默认24小时