两种时间更新机制
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