Android的自动对时有好几种,基本的是通过ntp网络服务器对时和通过手机sim卡运营商网络对时,国内如果是电信cdma手机的话,按照运营商需求是强制通过第二种方式对时的,手机设置中不可选择取消自动对时。不过目前手机基本都是全网通了,所以这个限制好像取消了。mtk的还有通过gps对时的选项。
ntp介绍见百度百科:
NTP服务器【Network Time Protocol(NTP)】是用来使计算机时间同步化的一种协议,它可以使计算机对其服务器或时钟源(如石英钟,GPS等等)做同步化,它可以提供高精准度的时间校正(LAN上与标准间差小于1毫秒,WAN上几十毫秒),且可介由加密确认的方式来防止恶毒的协议攻击。时间按NTP服务器的等级传播。按照离外部UTC源的远近把所有服务器归入不同的Stratum(层)中。
ntp对时服务器可见http://www.ntp.org.cn/,Android源码默认服务器不是中国区的,android是通过SettingProvider设置对时服务器值的,设置中的大多数值其实都是SettingsProvider数据库某个值。
Android系统服务器的值是在NtpTrustedTime类中初始化的。
frameworks/base/core/java/android/util/NtpTrustedTime.java
public static synchronized NtpTrustedTime getInstance(Context context) {
if (sSingleton == null) {
final Resources res = context.getResources();
final ContentResolver resolver = context.getContentResolver();
final String defaultServer = res.getString(
com.android.internal.R.string.config_ntpServer);
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);
final String server = secureServer != null ? secureServer : defaultServer;
sSingleton = new NtpTrustedTime(server, timeout);
sContext = context;
}
return sSingleton;
}
可以修改Settings.Global.NTP_SERVER数据库值来设置ntp服务器地址。之前有过自动对时无效的问题,后来修改ntp服务器地址为中国区的就没有再出现过这个问题了。注意通过网络对时是需要连接网络的,数据连接或者wifi都是可以的,不联网肯定没用啊...
android系统在设置中可以选择自动对时,自动对时设置界面代码是DateTimeSettings,启用自动对时的起点就是控件的点击事件
packages/apps/Settings/src/com/android/settings/DateTimeSettings.java
Settings.Global.putInt(getContentResolver(),
Settings.Global.AUTO_TIME, 1);
控件点击事件中写入了Settings.Global.AUTO_TIME值,使用自动同步的时候值为1
对时服务创建于SystemServer的run()函数,SystemServer的启动流程网上有不少分析了,不赘述
/frameworks/base/services/java/com/android/server/SystemServer.java
private void run() {
...
startOtherServices();
...
}
调用startOtherServices
private void startOtherServices() {
...
networkTimeUpdater = new NetworkTimeUpdateService(context);
...
if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
...
}
创建了NetworkTimeUpdateService对象并调用了systemRunning
public void systemRunning() {
...
mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
mSettingsObserver.observe(mContext);
...
}
创建了一个数据库监控Observer监控Settings.Global.AUTO_TIME值的变化,值有变化就发送到内部的handler类对象中进行处理,处理函数是:
private void onPollNetworkTime(int event)
该方法稍长,不过基本逻辑就是先获取ntp时间,然后设置时间。
该方法最终调用SystemClock类中方法设置系统时间
SystemClock.setCurrentTimeMillis(ntp);
手机不用通过数据网络照样可以对时的,例如电信手机开机后应该可以看到系统会发送自动对时的notification。
以Cdma网络为例,代码在CdmaServiceStateTracker.java,该类中有两条路径设置时间,路径1同样是监听Settings.Global.AUTO_TIME值
private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
if (DBG) log("Auto time state changed");
revertToNitzTime();
}
};
监听到值有变化的时候调用revertToNitzTime
private void revertToNitzTime() {
...
setAndBroadcastNetworkSetTime(mSavedTime
+ (SystemClock.elapsedRealtime() - mSavedAtTime));
...
}
revertToNitzTime调用setAndBroadcastNetworkSetTime
private void setAndBroadcastNetworkSetTime(long time) {
if (DBG) log("setAndBroadcastNetworkSetTime: time=" + time + "ms");
SystemClock.setCurrentTimeMillis(time);
Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra("time", time);
mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
setAndBroadcastNetworkSetTime设置时间并发送了一个广播。路径1的流程到此完毕
路径2是ril上报时间,从而导致更新。在CdmaServiceStateTracker构造函数中
mCi.setOnNITZTime(this, EVENT_NITZ_TIME, null);
向ril注册了监听时间,ril有对时消息上报的时候会通知CdmaServiceStateTracker,telephony framework中的xxTracker基本都是继承Handler类的,目的就是为了方便发送和接收消息
case EVENT_NITZ_TIME:
ar = (AsyncResult) msg.obj;
String nitzString = (String)((Object[])ar.result)[0];
long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue();
setTimeFromNITZString(nitzString, nitzReceiveTime);
收到EVENT_NITZ_TIME消息后调用setTimeFromNITZString:
private void setTimeFromNITZString (String nitz, long nitzReceiveTime){
...
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
...
if (getAutoTime()) {
...
setAndBroadcastNetworkSetTime(c.getTimeInMillis());
...
}
...
}
调用路径1末尾同样的方法设置时间。gsm的代码和cdma类似。不过依据我的个人经验,国内好像只有电信才有这个功能。