NITZ:NITZ(Network Identity and Time Zone)或网络标识和时区,是一种用于自动配置本地的时间和日期的机制,同时也通过无线网向移动设备提供运营商信息。NITZ是自从PHASE 2+ RELEASE 96 的GSM中的可选功能,经常被用来自动更新移动电话的系统时钟。
NTP:NTP(Network Time Protocol)提供准确时间,首先要有准确的时间来源,这一时间应该是国际标准时间UTC。 NTP获得UTC的时间来源可以是原子钟、天文台、卫星,也可以从Internet上获取。这样就有了准确而可靠的时间源。时间按NTP服务器的等级传播。
1.在setting中勾选“自动确定时间和日期”,“自动确定时区”后只是对key值为AUTO_TIME和AUTO_TIME_ZONE的Preference进行了赋值.
源码路径:packages/apps/Settings/src/com/android/settings/DateTimeSettings.java
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference.getKey().equals(KEY_AUTO_TIME)) {
/// M: modify as MTK add GPS time Sync feature @{
int index = Integer.parseInt(newValue.toString());
boolean autoEnabled = true;
if (index == AUTO_TIME_NETWORK_INDEX) {
Settings.Global.putInt(getContentResolver(),
Settings.Global.AUTO_TIME, 1);
Settings.Global.putInt(getContentResolver(),
Settings.System.AUTO_TIME_GPS, 0);
} else if (index == AUTO_TIME_GPS_INDEX) {
showDialog(DIALOG_GPS_CONFIRM);
setOnCancelListener(this);
} else {
Settings.Global.putInt(getContentResolver(), Settings.Global.AUTO_TIME, 0);
Settings.Global.putInt(getContentResolver(), Settings.System.AUTO_TIME_GPS, 0);
autoEnabled = false;
}
mAutoTimePref.setSummary(mAutoTimePref.getEntries()[index]);
/// @}
mTimePref.setEnabled(!autoEnabled);
mDatePref.setEnabled(!autoEnabled);
} else if (preference.getKey().equals(KEY_AUTO_TIME_ZONE)) {
boolean autoZoneEnabled = (Boolean) newValue;
Settings.Global.putInt(
getContentResolver(), Settings.Global.AUTO_TIME_ZONE, autoZoneEnabled ? 1 : 0);
mTimeZone.setEnabled(!autoZoneEnabled);
}
return true;
}
2.对于自动对时操作,Android启动了一个NetworkTimeUpdateService系统服务去做时间的更新操作,NetworkTimeUpdateService对上述的key值进行了监听,在检测到key值改变的时候,就会发送消息mHandler.obtainMessage(mMsg).sendToTarget();
frameworks\base\services\java\com\android\server\SystemServer.java
private void startOtherService(){
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);
}
race.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
当NetworkTimeUpdateService启动,实例化对象不为空时,调用systemRunning()方法
try {
if (networkTimeUpdaterF != null)
networkTimeUpdaterF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying NetworkTimeService running", e);
}
systemRunning做了什么东西呢?我们后续给出分析
对于自动对时有一些属性是定义在了系统资源中,在config.xml中定义了一些属性
a) frameworks\base\core\res\res\values\config.xml
通过设置监听器对Settings中的Preference的点击事件进行监听
frameworks\base\services\core\java\com\android\server\NetworkTimeUpdateService.java
2.1在构造方法中获取NtpTrustedTime的实例并获取NTP服务器的address,timeout等参数
mTime = NtpTrustedTime.getInstance(context);
//M: For multiple NTP server retry
mDefaultServer = ((NtpTrustedTime) mTime).getServer();
mNtpServers.add(mDefaultServer);
for (String str : SERVERLIST)
{
mNtpServers.add(str);
}
mTryAgainCounter = 0;
2.2获取广播传输内容
Intent pollIntent = new Intent(ACTION_POLL, null);
mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
2.3我们前面在config.xml中设置的参数在这里会被读取出来
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);
2.4获取电源管理,保持CPU的运转
mWakeLock = ((PowerManager)
context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, TAG);
3.NTUS(NetworkTimeUpdateService)的构造方法分析完了,接着是分析前面在SystemServer中启动的systemRunning()方法
3.1这个方法主要工作是初始化一些receivers和初始化第一次NTP请求
//ACTION_NETWORK_SET_TIME和ACTION_NETWORK_SET_TIMEZONE
registerForTelephonyIntents();
//mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
registerForAlarms();
//EVENT_NETWORK_CHANGED和NETWORK_CHANGE_EVENT_DELAY_MS
registerForConnectivityIntents();
3.2开启一个新的线程对网络时间进行监测
HandlerThread thread = new HandlerThread(TAG);
thread.start();
mHandler = new MyHandler(thread.getLooper());
// Check the network time on the new thread
Handler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
3.3对手机设置中的自动对时preference安排观察者进行监听
mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
mSettingsObserver.observe(mContext);
现在最新的还加入了对GPS事件的监听此次分析不包括这部分内容
4.分析到这里是看不到我们怎么做出系统更新操作的。这里要插入另外一个状态监听的类ServiceStateTracker
public ServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) {
initOnce(phone, ci);
updatePhoneType();
}
4.1InitOnce()方法应该是只初始化一次的方法,只关心和自动对时有关的
mCr.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
mAutoTimeObserver);
mCr.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
mAutoTimeZoneObserver);
也是设置了两个观察者对设置里面的两个preference进行监听
private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
Rlog.i(LOG_TAG, "Auto time state changed");
revertToNitzTime();
}
};
private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
Rlog.i(LOG_TAG, "Auto time zone state changed");
revertToNitzTimeZone();
}
};
4.2//判断设置的“自动对时”是否打开,不打开直接返回啥也不做
//如果是打开的则发送广播ACTION_NETWORK_SET_TIME
//这个广播的处理是在NetworkTimeUpdateService中处理的
private void revertToNitzTime() {
if (Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME, 0) == 0) {
log("[NITZ]:revertToNitz,AUTO_TIME is 0");
return;
}
if (DBG) {
log("Reverting to NITZ Time: mSavedTime=" + mSavedTime + " mSavedAtTime=" +
mSavedAtTime + " tz='" + mSavedTimeZone + "'");
}
if ((mSavedTime != 0 && mSavedAtTime != 0)&&!isGioneeAutoTimeSuccess()) {
//if (mSavedTime != 0 && mSavedAtTime != 0) {
setAndBroadcastNetworkSetTime(mSavedTime
+ (SystemClock.elapsedRealtime() - mSavedAtTime));
}
}
更新时区的操作和上面的逻辑是一样的,不重复分析。
5.这个类没有直接对更新时间和时区进行操作,只是告知NetworkTimeUpdateService是否需要进行更新的动作。
我们重新回到NetworkTimeUpdateService.java
还记得我们在systemRunning方法中起了一个线程吗?
HandlerThread thread = new HandlerThread(TAG);
thread.start();
mHandler = new MyHandler(thread.getLooper());
// Check the network time on the new thread
mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
在new的MyHandle中可以看到,我们在ServiceStateTracker中发送的广播会在这里面接收并给出处理逻辑
/** 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:
if (DBG) Log.d(TAG, "MyHandler::handleMessage what = " + msg.what);
onPollNetworkTime(msg.what);
break;
/// M: comment @{ add GPS Time Sync Service
case EVENT_GPS_TIME_SYNC_CHANGED:
boolean gpsTimeSyncStatus = getGpsTimeSyncState();;
Log.d(TAG, "GPS Time sync is changed to " + gpsTimeSyncStatus);
onGpsTimeChanged(gpsTimeSyncStatus);
break;
/// @}
}
}
}
我们可以看到
case EVENT_AUTO_TIME_CHANGED:
case EVENT_POLL_NETWORK_TIME:
case EVENT_NETWORK_CHANGED:
这三个消息走的都是
onPollNetworkTime(int event)方法
private void onPollNetworkTime(int event) {
if (DBG) Log.d(TAG, "onPollNetworkTime start");
// If Automatic time is not set, don't bother.
if (!isAutomaticTimeRequested()) //判断设置中自动对时有没有勾选上,没有则直接返回
return;
if (DBG) Log.d(TAG, "isAutomaticTimeRequested() = True");
mWakeLock.acquire();
try {
onPollNetworkTimeUnderWakeLock(event);
} finally {
mWakeLock.release();
}
}
6.分析终点到了,onPollNetworkTimeUnderWakeLock(event);这里会判断网络的状态之后根据当前系统的时间和获取到的时间做对比
在onPollNetworkTime方法中先判断是否勾选“自动更新时间”,如果没勾选直接退出,如果勾选了再看,如果更新的NITZ时间不为NOT_SET(-1),且更新间隔小于mPollingIntervalMs,mPollingIntervalMs=12 小时,那么就直接更新NITZ的时间,否则用NTP同步时间。
当从时间服务器上获取的NTP时间和当前时间之差的绝对值大于一个阀值,系统认为当前时间错误,需要更新时间。
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 (DBG) Log.d(TAG, "mNitzTimeSetTime: " + mNitzTimeSetTime + ",refTime: " + refTime);
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
// when autotime changed force refresh ntp for change time
if ((mTime.getCacheAge() >= mPollingIntervalMs)||
(event == EVENT_AUTO_TIME_CHANGED)) {
//M: For multiple NTP server retry
mTime.forceRefresh();
int index = mTryAgainCounter % mNtpServers.size();
if (DBG) Log.d(TAG, "mTryAgainCounter = " + mTryAgainCounter
+ ";mNtpServers.size() = " + mNtpServers.size()
+ ";index = " + index + ";mNtpServers = " + mNtpServers.get(index));
if (mTime instanceof NtpTrustedTime)
{
((NtpTrustedTime) mTime).setServer(mNtpServers.get(index));
mTime.forceRefresh();
((NtpTrustedTime) mTime).setServer(mDefaultServer);
}
else
{
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);
}
notifyStatus(ntp, currentTime);
SystemProperties.set("gionee.ignore-nitz","yes");
mLastNtpFetchTime = SystemClock.elapsedRealtime();
} else {
Log.d(TAG, "fail : mTryAgainCounter " + mTryAgainCounter);
if (isNetworkConnected()) {
if (DBG) Log.d(TAG, "isNetworkConnected() = true");
mTryAgainCounter++;
if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
// Try again shortly
resetAlarm(mPollingIntervalShorterMs);
} else {
// Try much later
if (DBG) Log.d(TAG, "mNitzTimeSetTime: " + mNitzTimeSetTime + ",refTime: " + refTime);
if (mNitzTimeSetTime == NOT_SET || refTime - mNitzTimeSetTime >= mPollingIntervalMs) {
notifyStatusFalil();
}
mTryAgainCounter = 0;
resetAlarm(mPollingIntervalMs);
}
} else {
if (DBG) Log.d(TAG, "isNetworkConnected() = false");
mTryAgainCounter = 0;
resetAlarm(mPollingIntervalMs);
}
return;
}
}
resetAlarm(mPollingIntervalMs);
}
总结:
如果时间自动同步选项未勾选,直接返回;
如果NITZ已同步且上次NITZ同步未超过12小时,则设置定时器12小时后再触发同步,即广播NetworkTimeUpdateService.ACTION_POLL;
如果NTP上次成功同步超过12小时或用户勾选自动同步选项,则进行下面的NTP同步,否则同上设置定时器12小时后再触发同步;
如果上次NTP成功同步超过12小时,则发起同步mTime.forceRefresh();
如果同步成功,获取此刻NTP时间ntp=mTime.currentTimeMillis();
如果同步时间与当前本机时间误差超过指定值阀值,则把ntp设置为本机时间SystemClock.setCurrentTimeMillis(ntp)