首先我们要明白下概念:
NITZ(Network identifier Time Zone:网络标识和时区):用来配置本地日期和时间的机制,也通过无线网络向设备提供运营商信息。常用来更新设备的系统时钟,可校正时间和时区。需要运营商支持(如果不支持的话就需要采用SNTP更新时间了)
SNTP(Simple Network Time protocol:简单网络时间协议):用于使用Internet时间校正设备时间,只能校正时间,无法校正时区
代码位置:http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/
NetworkTimeUpdateService.java
在systemService里面初始化的 NetworkTimeUpdateService,看下初始化做了啥
public NetworkTimeUpdateService(Context context) {
mContext = context;
mTime = NtpTrustedTime.getInstance(context); ---> 拿到 NtpTrustedTime对象
mAlarmManager = mContext.getSystemService(AlarmManager.class);
mTimeDetector = mContext.getSystemService(TimeDetector.class);
mCM = mContext.getSystemService(ConnectivityManager.class);
Intent pollIntent = new Intent(ACTION_POLL, null);
// Broadcast alarms sent by system are immutable
mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, PendingIntent.FLAG_IMMUTABLE);
// 注意这里的三个变量
// mPollingIntervalMs 是64800000毫秒,就是18个小时
// mPollingIntervalShorterMs 是60000毫秒,就是1分钟
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);
mWakeLock = context.getSystemService(PowerManager.class).newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, TAG);
}
<!-- Normal polling frequency in milliseconds -->
<integer name="config_ntpPollingInterval">64800000</integer>
<!-- Try-again polling interval in milliseconds, in case the network request failed -->
<integer name="config_ntpPollingIntervalShorter">60000</integer>
<!-- Number of times to try again with the shorter interval, before backing off until the normal polling interval.
A value < 0 indicates infinite. -->
<integer name="config_ntpRetry">3</integer>
<!-- Timeout to wait for NTP server response in milliseconds. -->
<integer name="config_ntpTimeout">5000</integer>
/** Initialize the receivers and initiate the first NTP request */
public void systemRunning() {
registerForAlarms(); ---> 注册广播,看下面贴的代码
HandlerThread thread = new HandlerThread(TAG);
thread.start();
mHandler = new MyHandler(thread.getLooper());
mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback(); ---> 网络变化的回调,看下面贴的代码
mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler,
EVENT_AUTO_TIME_ENABLED);
mAutoTimeSettingObserver.observe(); ---> 注册内容观察者,看下面贴的代码
}
private void registerForAlarms() {
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget(); ---> 发送这个消息:EVENT_POLL_NETWORK_TIME
}
}, new IntentFilter(ACTION_POLL)); ---> com.android.server.NetworkTimeUpdateService.action.POLL
}
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;
}
}
private static class AutoTimeSettingObserver extends ContentObserver {
private final Context mContext;
private final int mMsg;
private final Handler mHandler;
AutoTimeSettingObserver(Context context, Handler handler, int msg) {
super(handler);
mContext = context;
mHandler = handler;
mMsg = msg;
}
// 需要监视的 AUTO_TIME
void observe() {
ContentResolver resolver = mContext.getContentResolver();
//Settings中的AUTO_TIME开关打开就会触发
resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME), false, this);
}
@Override
public void onChange(boolean selfChange) {
if (isAutomaticTimeEnabled()) {
mHandler.obtainMessage(mMsg).sendToTarget();
}
}
/**
* Checks if the user prefers to automatically set the time.
*/
private boolean isAutomaticTimeEnabled() {
ContentResolver resolver = mContext.getContentResolver();
return Settings.Global.getInt(resolver, Settings.Global.AUTO_TIME, 0) != 0;
}
}
通过上面这段代码我们知道两点
- 网络发生变化时候会发 EVENT_NETWORK_CHANGED 消息
- 用户设置AUTO_TIME变化时会发 EVENT_AUTO_TIME_ENABLED 消息
- 收到广播后发送 EVENT_POLL_NETWORK_TIME 消息
而这些消息都在Handle里面处理:
/** Handler to do the network accesses on */
private class MyHandler extends Handler {
MyHandler(Looper l) {
super(l);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_AUTO_TIME_ENABLED:
case EVENT_POLL_NETWORK_TIME:
case EVENT_NETWORK_CHANGED:
onPollNetworkTime(msg.what); ---> 这三个消息都触发这个方法
break;
}
}
}
private void onPollNetworkTime(int event) {
if (mDefaultNetwork == null) return; ---> 上面在网络回调中onAvailable方法里给这个赋值了,如果依然是Null,则返回
mWakeLock.acquire();
// 也就是如果没有网络变换,mDefaultNetwork永远是null,就算其它两个消息来了,也是得return,不会走到下面
try {
onPollNetworkTimeUnderWakeLock(event);
} finally {
mWakeLock.release();
}
}
private void onPollNetworkTimeUnderWakeLock(int event) {
long currentElapsedRealtimeMillis = SystemClock.elapsedRealtime();
// Force an NTP fix when outdated
NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
// 这里就是涉及到我们一开始讲的参数了
// mPollingIntervalMs 是64800000毫秒,就是18个小时
// mPollingIntervalShorterMs 是60000毫秒,就是1分钟
// 刚开始 cachedNtpResult 是 null,所以回去强制 forceRefresh()下
if (cachedNtpResult == null || cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis) >= mPollingIntervalMs) {
if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
//位置:http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/core/java/android/util/NtpTrustedTime.java#forceRefresh()
boolean isSuccessful = mTime.forceRefresh(); ---> 这里面就是去和NTP服务器交互的地方
if (isSuccessful) {
mTryAgainCounter = 0;
} else {
String logMsg = "forceRefresh() returned false: cachedNtpResult=" + cachedNtpResult
+ ", currentElapsedRealtimeMillis=" + currentElapsedRealtimeMillis;
if (DBG) {
Log.d(TAG, logMsg);
}
mLocalLog.log(logMsg);
}
// 此时再获取一次,正常来说就不是null了,ntp 服务没问题的话
cachedNtpResult = mTime.getCachedTimeResult();
}
/*
* cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis) 是返回的时间差
* 当前时间减去服务器返回的参考时间
*/
if (cachedNtpResult != null && cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis) < mPollingIntervalMs) {
// Obtained fresh fix; schedule next normal update
resetAlarm(mPollingIntervalMs - cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis));
// 将时间设置到系统中
makeNetworkTimeSuggestion(cachedNtpResult, "Origin: NetworkTimeUpdateService. event=" + event);
} else {
// No fresh fix; schedule retry
// 从这个注释我们就知道如果没有正常更新,就要尝试mTryAgainTimesMax是3
mTryAgainCounter++; ---> 从0累计
if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
resetAlarm(mPollingIntervalShorterMs); ---> 1分钟
} else {
// Try much later
String logMsg = "mTryAgainTimesMax exceeded, cachedNtpResult=" + cachedNtpResult;
if (DBG) {
Log.d(TAG, logMsg);
}
mLocalLog.log(logMsg);
mTryAgainCounter = 0;
resetAlarm(mPollingIntervalMs);
}
}
// 设置闹钟,经过 interval 时间后 发一个 mPendingPollIntent
private void resetAlarm(long interval) {
mAlarmManager.cancel(mPendingPollIntent);
long now = SystemClock.elapsedRealtime();
long next = now + interval;
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
}
从开头我们知道 mPendingPollIntent 是这个,而 ACTION_POLL = “com.android.server.NetworkTimeUpdateService.action.POLL”;
接收广播的地方开头代码也贴出来了,这样就会发送 EVENT_POLL_NETWORK_TIME 消息,在重复调用Handle去处理,这样又回到之前的流程了,尝试3次。
Ok,到此我们就把整个Wi-Fi更新时间的流程给介绍完毕,说白了就是访问NTP的服务器去获取时间,和服务器交互的代码,有兴趣的可以去看看:http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/core/java/android/util/NtpTrustedTime.java#forceRefresh()