Android系统时间更新有两种方式:NITZ时间和NTP时间,NITZ由运营商提供,通过sim卡更新时间,NTP为通过网络,访问服务器获取时间,这里主要介绍NTP的方式。
NetworkTimeUpdateService启动
NetworkTimeUpdateService作为Android系统服务,在SystemServer中启动
private void startOtherServices() {
......
NetworkTimeUpdateService networkTimeUpdater = null;
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();
}
......
final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
......
try {
if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying NetworkTimeService running", e);
}
......
}
SystemServer中创建了NetworkTimeUpdateService,并注册到ServiceManager以供client端调用,接着调用NetworkTimeUpdateService的systemRunning方法
我们先看NetworkTimeUpdateService构造方法
public NetworkTimeUpdateService(Context context) {
mContext = context;
//获取NtpTrustedTime实例
mTime = NtpTrustedTime.getInstance(context);
//创建AlarmManager
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
//创建PendingPollIntent以供后面AlarmManager定时设置
Intent pollIntent = new Intent(ACTION_POLL, null);
mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
//mPollingIntervalMs代表请求ntp服务器的频率
//从config配置文件读取,86400000ms,就是一天
mPollingIntervalMs = mContext.getResources().getInteger(
com.android.internal.R.integer.config_ntpPollingInterval);
//mPollingIntervalShorterMs代表请求ntp服务器失败后再次
//请求的时间间隔,为60000ms,就是1分钟
mPollingIntervalShorterMs = mContext.getResources().getInteger(
com.android.internal.R.integer.config_ntpPollingIntervalShorter);
//mTryAgainTimesMax 代表请求ntp服务器失败后再次尝试的
//最大次数,为3次
mTryAgainTimesMax = mContext.getResources().getInteger(
com.android.internal.R.integer.config_ntpRetry);
//mTimeErrorThresholdMs代表请求ntp服务器得到的时间
//和当前时间差大于此阈值,则更新时间。
mTimeErrorThresholdMs = mContext.getResources().getInteger(
com.android.internal.R.integer.config_ntpThreshold);
mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, TAG);
}
NetworkTimeUpdateService构造函数中初始化了各种值
NtpTrustedTime
NtpTrustedTime通过单例获取,主要看下ntp服务器
public static synchronized NtpTrustedTime getInstance(Context context) {
if (sSingleton == null) {
final Resources res = context.getResources();
final ContentResolver resolver = context.getContentResolver();
//默认ntp服务器,从配置文件读取,time.android.com
//这是google的服务器,国内用户如果请求这个服务器可能无法更新时间
final String defaultServer = res.getString(
com.android.internal.R.string.config_ntpServer);
//timeout为5s
final long defaultTimeout = res.getInteger(
com.android.internal.R.integer.config_ntpTimeout);
final String secureServer = Settings.Global.getString(
resolver, Settings.Global.NTP_SERVER);
final long timeout = Settings.Global.getLong(
resolver, Settings.Global.NTP_TIMEOUT, defaultTimeout);
//默认都会使用配置文件中配置的ntp服务器
final String server = secureServer != null ? secureServer : defaultServer;
sSingleton = new NtpTrustedTime(server, timeout);
sContext = context;
}
return sSingleton;
}
systemRunning
接着看NetworkTimeUpdateService初始化完成后调用了systemRunning方法
public void systemRunning() {
//注册telephony的广播
registerForTelephonyIntents();
//注册Alarm发送的广播
registerForAlarms();
//注册网络改变的广播
registerForConnectivityIntents();
HandlerThread thread = new HandlerThread(TAG);
thread.start();
mHandler = new MyHandler(thread.getLooper());
//发送EVENT_POLL_NETWORK_TIME消息
mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
/*
创建SettingsObserver,监听Settings中AUTO_TIME
值的变化
*/
mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
mSettingsObserver.observe(mContext);
}
/*
TelephonyIntents.ACTION_NETWORK_SET_TIME 和
TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE 广播来监听来自运营商
的时间改变
*/
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);
}
/*
注册自定义的广播,ACTION_POLL由Alarm定时器来发送,
在NetworkTimeUpdateService构造方法中创建了mPendingPollIntent
供Alarm发送
*/
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));
}
/*
监听网络发生变化的广播
*/
private void registerForConnectivityIntents() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.registerReceiver(mConnectivityReceiver, intentFilter);
}
SettingsObserver
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();
//监听AUTO_TIME的值,也就是Settings中自动更新网络时间的开关
resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
false, this);
}
@Override
public void onChange(boolean selfChange) {
//当AUTO_TIME变化时发送EVENT_POLL_NETWORK_TIME消息
mHandler.obtainMessage(mMsg).sendToTarget();
}
}
看一下mHandler,其实下面三个消息都会触发请求ntp服务器的方法,下面来挨着看下这三个消息的发送条件
private static final int EVENT_AUTO_TIME_CHANGED = 1;
private static final int EVENT_POLL_NETWORK_TIME = 2;
private static final int EVENT_NETWORK_CHANGED = 3;
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;
}
}
}
EVENT_AUTO_TIME_CHANGED
这个就是在SettingsObserver的onChange方法里发送的,当我们更新Settings中的AUTO_TIME开光就会触发onPollNetworkTime更新时间,但是具体是否更新时间还要看onPollNetworkTime的一些判断,等下再说
EVENT_POLL_NETWORK_TIME
这个消息在systemRunning方法和Alarms中发送,systemRunning我们知道在开机启动的时候调用,Alarms呢,Alarms其实是在onPollNetworkTimeUnderWakeLock方法中设置的,等下再说
EVENT_NETWORK_CHANGED
这个比较简单,就是监听到网络发生变化之后发送的
private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
if (DBG) Log.d(TAG, "Received CONNECTIVITY_ACTION ");
// Don't bother checking if we have connectivity, NtpTrustedTime does that for us.
Message message = mHandler.obtainMessage(EVENT_NETWORK_CHANGED);
// Send with a short delay to make sure the network is ready for use
mHandler.sendMessageDelayed(message, NETWORK_CHANGE_EVENT_DELAY_MS);
}
}
};
onPollNetworkTime
看了三种触发onPollNetworkTime的条件,接着看onPollNetworkTime这个方法具体实现
private void onPollNetworkTime(int event) {
// 如果没有开启自动更新时间直接return
if (!isAutomaticTimeRequested()) return;
mWakeLock.acquire();
try {
onPollNetworkTimeUnderWakeLock(event);
} finally {
mWakeLock.release();
}
}
onPollNetworkTimeUnderWakeLock
private void onPollNetworkTimeUnderWakeLock(int event) {
//从开机到现在的时间
final long refTime = SystemClock.elapsedRealtime();
//如果已经通过运营商更新了时间并且上次更新的时间到当前为止
//小于1天则设置一个Alarm定时器(定时器为1天后触发)并且return
if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
resetAlarm(mPollingIntervalMs);
return;
}
final long currentTime = System.currentTimeMillis();
// 如果没有设置过ntp时间或者开机但现在的时间大于等于最后一次请求ntp
//服务器加上1天的时间或者时因为Settings的自动更新开关触发的更新时间
if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
|| event == EVENT_AUTO_TIME_CHANGED) {
if (DBG) Log.d(TAG, "Before Ntp fetch");
// mTime.getCacheAge等于上次请求ntp服务器的时间
//如果大于等于1天则强制更新时间
if (mTime.getCacheAge() >= mPollingIntervalMs) {
mTime.forceRefresh();
}
// 如果上次请求ntp服务器的时间小于1天
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 {
//如果上次请求ntp服务器的时间已经大于1天了
mTryAgainCounter++;
//最多尝试次数为mTryAgainTimesMax,3次
if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
//尝试请求ntp服务器的时间间隔为mPollingIntervalShorterMs,1分钟
resetAlarm(mPollingIntervalShorterMs);
} else {
// 如果三次请求之后则需要在1天之后再请求ntp服务器
mTryAgainCounter = 0;
resetAlarm(mPollingIntervalMs);
}
return;
}
}
//保证无论如何都可以设置一个1天后请求ntp服务器的定时器
resetAlarm(mPollingIntervalMs);
}
private void resetAlarm(long interval) {
mAlarmManager.cancel(mPendingPollIntent);
long now = SystemClock.elapsedRealtime();
long next = now + interval;
//在next时间之后执行mPendingPollIntent,mPendingPollIntent在
//NetworkTimeUpdateService构造方法中初始化,作用时发送
//自定义的ACTION_POLL广播
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
}
mTime.forceRefresh()
NtpTrustedTime中的forceRefresh方法强制刷新时间
@Override
public boolean forceRefresh() {
//如果ntp服务器为空则返回
if (TextUtils.isEmpty(mServer)) {
// missing server, so no trusted time available
return false;
}
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();
//请求mServer,也就是time.android.com,5s超时
if (client.requestTime(mServer, (int) mTimeout)) {
mHasCache = true;
//请求成功之后获取的ntp时间
mCachedNtpTime = client.getNtpTime();
mCachedNtpElapsedRealtime = client.getNtpTimeReference();
mCachedNtpCertainty = client.getRoundTripTime() / 2;
return true;
} else {
return false;
}
}
client.requestTime
SntpClient中的requestTime方法发送socket请求获取服务器时间
public boolean requestTime(InetAddress address, int port, int timeout) {
DatagramSocket socket = null;
final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NTP);
try {
socket = new DatagramSocket();
socket.setSoTimeout(timeout);
byte[] buffer = new byte[NTP_PACKET_SIZE];
DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, 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
final long requestTime = System.currentTimeMillis();
final 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);
final long responseTicks = SystemClock.elapsedRealtime();
final long responseTime = requestTime + (responseTicks - requestTicks);
// extract the results
final byte leap = (byte) ((buffer[0] >> 6) & 0x3);
final byte mode = (byte) (buffer[0] & 0x7);
final int stratum = (int) (buffer[1] & 0xff);
final long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);
final long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
final long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
/* do sanity check according to RFC */
// TODO: validate originateTime == requestTime.
checkValidServerReply(leap, mode, stratum, transmitTime);
long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime);
long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2;
EventLogTags.writeNtpSuccess(address.toString(), roundTripTime, clockOffset);
if (DBG) {
Log.d(TAG, "round trip: " + roundTripTime + "ms, " +
"clock offset: " + clockOffset + "ms");
}
mNtpTime = responseTime + clockOffset;
mNtpTimeReference = responseTicks;
mRoundTripTime = roundTripTime;
} catch (Exception e) {
EventLogTags.writeNtpFailure(address.toString(), e.toString());
if (DBG) Log.d(TAG, "request time failed: " + e);
return false;
} finally {
if (socket != null) {
socket.close();
}
TrafficStats.setThreadStatsTag(oldTag);
}
return true;
}
请求完成之后获取的时间如何更新呢,回到NetworkTimeUpdateService,
mCachedNtpTime = client.getNtpTime();
private void onPollNetworkTimeUnderWakeLock(int event) {
......
if (mTime.getCacheAge() >= mPollingIntervalMs) {
mTime.forceRefresh();
}
//强制刷新时间之后
if (mTime.getCacheAge() < mPollingIntervalMs) {
//ntp请求服务器之后获取的时间
final long ntp = mTime.currentTimeMillis();
mTryAgainCounter = 0;
if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
|| mLastNtpFetchTime == NOT_SET) {
// Set the system time
if (DBG && mLastNtpFetchTime == NOT_SET
&& Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
//保证获取的网络时间不会超过最大值
if (ntp / 1000 < Integer.MAX_VALUE) {
//设置ntp时间到系统中
SystemClock.setCurrentTimeMillis(ntp);
}
} else {
if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
}
mLastNtpFetchTime = SystemClock.elapsedRealtime();
}
......
}
总结:触发onPollNetworkTimeUnderWakeLock的三种方式
1.Settings中的自动更新时间开关变化
2.Alarm定时器中发送自定义广播
3.网络发生变化
至于最终是否需要请求ntp服务器更新网络时间则需要一些判断,最主要的就是上次请求ntp服务器的时间间隔与1天对比,如果请求ntp失败时候又三次重试机会,如果再失败则必须等待一天后再请求