Android时间同步流程
对时概述
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);
sim卡网络对时
手机不用通过数据网络照样可以对时的,例如电信手机开机后应该可以看到系统会发送自动对时的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类似。不过依据我的个人经验,国内好像只有电信才有这个功能。
更多相关文章
- 万字长文带你了解最常用的开源 Squid 代理服务器
- Nginx系列教程(一)| 手把手教你在Linux环境下搭建Nginx服务
- Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
- Nginx系列教程(六)| 手把手教你搭建 LNMP 架构并部署天空网络电影
- Android(安卓)端实现mqtt消息接收
- Android编程之客户端通过socket与服务器通信的方法
- Android(安卓)HttpClient Session保持
- 2020-12-24
- 解决 android 4.2 连接 leap wifi 网络