Android时区和时间同步
来源:互联网 发布:双代号网络计划规则 编辑:程序博客网 时间:2024/06/05 19:41
概念
NITZ(Network identifier Time Zone:网络标识和时区):用来配置本地日期和时间的机制,也通过无线网络向设备提供运营商信息。常用来更新设备的系统时钟,可校正时间和时区。需要运营商支持(如果不支持的话就需要采用SNTP更新时间了)
SNTP(Simple Network Time protocol:简单网络时间协议):用于使用Internet时间校正设备时间,只能校正时间,无法校正时区
系统启动和主动设置功能流程
系统开机时间设置原理
frameworks\base\services\java\com\android\server\SystemServer.java
frameworks\base\services\java\com\android\server\NetworkTimeUpdateService.java
SystemServer包含init1和init2两个方法。init1用来加载JNI库,启动Native世界;init2启动了ServerThread,ServerThread中启动了framework世界;main方法中首先加载系统默认时间
public static void main(String[] args) { ... if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) { // If a device's clock is before 1970 (before 0), a lot of // APIs crash dealing with negative numbers, notably // java.io.File#setLastModified, so instead we fake it and // hope that time from cell towers or NTP fixes it // shortly. Slog.w(TAG, "System clock is before 1970; setting to 1970."); SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME); } System.loadLibrary("android_servers"); init1(args); } /** * This method is called from Zygote to initialize the system. This will cause the native * services (SurfaceFlinger, AudioFlinger, etc..) to be started. After that it will call back * up into init2() to start the Android services. */ native public static void init1(String[] args);
init1通过System.loadLibrary(“android-servers”)加载一个类库文件,其对应的源码文件为com_android_server_SystemServer.cpp 其C++代码如下,在该类库中转调了system_init()方法
// 类似java的抽象方法 extern "C" int system_init(); static void android_server_SystemServer_init1(JNIEnv* env, jobject clazz) { // 转调 system_init(); } /* * JNI registration. */ static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ // 函数指针 把init1方法映射到android_server_SystemServer_init1 { "init1", "([Ljava/lang/String;)V", (void*) android_server_SystemServer_init1 }, };
System_init方法在System_init.cpp中实现,它首先启动系统的硬件服务,比如Audio、Camera等,启动完硬件服务后它又通过Android运行时环境调用了SystemServer中的init2()方法,init2()方法启动Framework世界,代码如下:
extern "C" status_t system_init() { ... // 启动硬件的服务 if (strcmp(propBuf, "1") == 0) { // Start the SurfaceFlinger SurfaceFlinger::instantiate(); } AndroidRuntime* runtime = AndroidRuntime::getRuntime(); LOGI("System server: starting Android services.\n"); // 启动完硬件服务后,又回到Systemserver的init2方法 runtime->callStatic("com/android/server/SystemServer", "init2"); ... }
SystemServer的init2方法中调用了ServerThread,启动了framework世界
public static final void init2() { Slog.i(TAG, "Entered the Android system server!"); Thread thr = new ServerThread(); thr.setName("android.server.ServerThread"); thr.start(); }
ServerThread中启动了NetworkTimeUpdateService,并调用了systemReady方法
try { Slog.i(TAG, "NetworkTimeUpdateService"); networkTimeUpdater = new NetworkTimeUpdateService(context); } catch (Throwable e) { reportWtf("starting NetworkTimeUpdate service", e); }....final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;... try { if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemReady(); } catch (Throwable e) { reportWtf("making Network Time Service ready", e); }
systemReady()主要做了两件事:初始化了广播接受者和NTP请求:
/** Initialize the receivers and initiate the first NTP request */ public void systemReady() { registerForTelephonyIntents(); registerForAlarms(); registerForConnectivityIntents(); mThread = new HandlerThread(TAG); mThread.start(); mHandler = new MyHandler(mThread.getLooper()); // Check the network time on the new thread mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget(); mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED); mSettingsObserver.observe(mContext); }
myHandler是用来做什么的呢?看代码:
/** 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_CONNECTED: onPollNetworkTime(msg.what); break; } } }
handler处理了三个事件:“选中了自动更新日期和时间”,“请求网络时间”,“网络已连接”,这三个事件如果被触发,都会调用onPollNetworkTime方法。onPollNetworkTime方法中都实现了什么功能呢?
private void onPollNetworkTime(int event) { // If Automatic time is not set, don't bother. // 判断"自动确定日期和时间"是否选中,如果没有直接return if (!isAutomaticTimeRequested()) return; final long refTime = SystemClock.elapsedRealtime(); // If NITZ time was received less than POLLING_INTERVAL_MS time ago, // no need to sync to NTP. //判断NITZ时间已经上报,且上报时间距离现在小于POLLING_INTERVAL_MS(24h),不需要使用NTP同步时间 if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < POLLING_INTERVAL_MS) { resetAlarm(POLLING_INTERVAL_MS); return; } final long currentTime = System.currentTimeMillis(); if (DBG) Log.d(TAG, "System time = " + currentTime); // Get the NTP time //如果NTP第一次请求、自上次请求到现在大于POLLING_INTERVAL_MS(24h)、选中自动确定日期和时间则请求NTP if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + POLLING_INTERVAL_MS || event == EVENT_AUTO_TIME_CHANGED) { if (DBG) Log.d(TAG, "Before Ntp fetch"); // force refresh NTP cache when outdated //如果ntp请求缓冲到现在大于POLLING_INTERVAL_MS(24h),刷新NTP时间 if (mTime.getCacheAge() >= POLLING_INTERVAL_MS) { mTime.forceRefresh(); } // only update when NTP time is fresh // 如果ntp时间刷新了,则更新本地时间 if (mTime.getCacheAge() < POLLING_INTERVAL_MS) { 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. // 如果ntp请求到的时间和当前系统时间差大于5秒或第一次发起ntp请求,否则以本地时间为准 if (Math.abs(ntp - currentTime) > TIME_ERROR_THRESHOLD_MS || mLastNtpFetchTime == NOT_SET) { // Set the system time if (DBG && mLastNtpFetchTime == NOT_SET && Math.abs(ntp - currentTime) <= TIME_ERROR_THRESHOLD_MS) { 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 // 设置ntp时间为系统本地时间 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 { // Try again shortly mTryAgainCounter++; if (mTryAgainCounter <= TRY_AGAIN_TIMES_MAX) { resetAlarm(POLLING_INTERVAL_SHORTER_MS); } else { // Try much later mTryAgainCounter = 0; resetAlarm(POLLING_INTERVAL_MS); } return; } } resetAlarm(POLLING_INTERVAL_MS); }
整体流程就是:
1.判断是否选中“自动确定日期和时间”,如果没有,直接return
2.判断NITZ时间是否已经上报,且上报时间距离现在小于POLLING_INTERVAL_MS(24h),如果是的话采用NITZ世界,不需要使用NTP同步时间
3.如果NTP请求缓冲到现在大于POLLING_INTERVAL_MS(24h),刷新NTP时间
4.如果ntp时间刷新了,则更新本地时间
5.如果ntp请求到的时间和当前系统时间差大于5秒或第一次发起NTP请求,更新本地时间,否则以本地时间为准
主动设置“自动确定日期和时间”与“自动确定时区”原理
进入”设置“—”日期和时间“,界面如下:
我们先看看”自动确定日期和时间“的实现:
device\softwinner\common\packages\TvdSettings\src\com\android\settings\DateTimeSettings.java
boolean autoTimeEnabled = getAutoState(Settings.Global.AUTO_TIME); boolean autoTimeZoneEnabled = getAutoState(Settings.Global.AUTO_TIME_ZONE); Intent intent = getActivity().getIntent(); boolean isFirstRun = intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false); mDummyDate = Calendar.getInstance(); mAutoTimePref = (CheckBoxPreference) findPreference(KEY_AUTO_TIME); mAutoTimePref.setChecked(autoTimeEnabled); mAutoTimeZonePref = (CheckBoxPreference) findPreference(KEY_AUTO_TIME_ZONE); // Override auto-timezone if it's a wifi-only device or if we're still in setup wizard. // TODO: Remove the wifiOnly test when auto-timezone is implemented based on wifi-location. if (Utils.isWifiOnly(getActivity()) || isFirstRun) { getPreferenceScreen().removePreference(mAutoTimeZonePref); autoTimeZoneEnabled = false; } mAutoTimeZonePref.setChecked(autoTimeZoneEnabled);
“自动确定网络时间”和“自动确定时区”使用的都是CheckBoxPreference实现并保存状态的。当状态改变时,会触发:
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { if (key.equals(KEY_DATE_FORMAT)) { String format = preferences.getString(key, getResources().getString(R.string.default_date_format)); Settings.System.putString(getContentResolver(), Settings.System.DATE_FORMAT, format); updateTimeAndDateDisplay(getActivity()); } else if (key.equals(KEY_AUTO_TIME)) { boolean autoEnabled = preferences.getBoolean(key, true); Settings.Global.putInt(getContentResolver(), Settings.Global.AUTO_TIME, autoEnabled ? 1 : 0); mTimePref.setEnabled(!autoEnabled); mDatePref.setEnabled(!autoEnabled); } else if (key.equals(KEY_AUTO_TIME_ZONE)) { boolean autoZoneEnabled = preferences.getBoolean(key, true); Settings.Global.putInt( getContentResolver(), Settings.Global.AUTO_TIME_ZONE, autoZoneEnabled ? 1 : 0); mTimeZone.setEnabled(!autoZoneEnabled); } }
结果只是实现了
Settings.Global.putInt(getContentResolver(), Settings.Global.AUTO_TIME,autoEnabled ? 1 : 0);
和
Settings.Global.putInt(getContentResolver(), Settings.Global.AUTO_TIME_ZONE, autoZoneEnabled ? 1 : 0);
继续搜索代码,发现在NetworkTimeUpdateService.java 和GsmServiceStateTracker.java中实现了监听:
frameworks\base\services\java\com\android\server\NetworkTimeUpdateService.java
/** Observer to watch for changes to the AUTO_TIME setting */ 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(); resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME), false, this); } @Override public void onChange(boolean selfChange) { mHandler.obtainMessage(mMsg).sendToTarget(); } }
frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm\GsmServiceStateTracker.java
cr = phone.getContext().getContentResolver(); cr.registerContentObserver( Settings.System.getUriFor(Settings.System.AUTO_TIME), true, mAutoTimeObserver); cr.registerContentObserver( Settings.System.getUriFor(Settings.System.AUTO_TIME_ZONE), true, mAutoTimeZoneObserver);
我们先看看GsmServiceStateTracker.java中的实现流程:
private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { Log.i("GsmServiceStateTracker", "Auto time state changed"); revertToNitzTime(); } }; private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { Log.i("GsmServiceStateTracker", "Auto time zone state changed"); revertToNitzTimeZone(); } };
跟踪revertToNitzTime()和revertToNitzeTimeZone()
private void revertToNitzTime() { if (Settings.System.getInt(phone.getContext().getContentResolver(), Settings.System.AUTO_TIME, 0) == 0) { return; } if (DBG) { log("Reverting to NITZ Time: mSavedTime=" + mSavedTime + " mSavedAtTime=" + mSavedAtTime); } if (mSavedTime != 0 && mSavedAtTime != 0) { setAndBroadcastNetworkSetTime(mSavedTime + (SystemClock.elapsedRealtime() - mSavedAtTime)); } } private void revertToNitzTimeZone() { if (Settings.System.getInt(phone.getContext().getContentResolver(), Settings.System.AUTO_TIME_ZONE, 0) == 0) { return; } if (DBG) log("Reverting to NITZ TimeZone: tz='" + mSavedTimeZone); if (mSavedTimeZone != null) { setAndBroadcastNetworkSetTimeZone(mSavedTimeZone); } }
首先判断是否选中自动确定,如果否的话直接return。是的话我们看到最后如果符合if判断的话,会发出广播:
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); phone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL); }
首先设置了系统时间,然后发出广播。跟踪TelephonyIntents.ACTION_NETWORK_SET_TIME发现在NetworkTimeUpdateService.java中进行了监听:
/** Receiver for Nitz time events */ private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) { mNitzTimeSetTime = SystemClock.elapsedRealtime(); } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) { mNitzZoneSetTime = SystemClock.elapsedRealtime(); } } };
只是修改了mNitzTimeSetTime(NITZ上报的时间)。
接下来我们进入NetworkTimeUpdateService.java查看实现流程:
/** Observer to watch for changes to the AUTO_TIME setting */ 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(); resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME), false, this); } @Override public void onChange(boolean selfChange) { mHandler.obtainMessage(mMsg).sendToTarget(); } }
当“自动确定日期和时间”状态发生改变时,mHandler又会触发,又跳转到了上面说的onPollNetworkTime(int event)方法;
参考资料:
同步时间:http://www.eoeandroid.com/thread-575174-1-1.html?_dsign=f33cbc51
Android 时间更新机制之网络更新时间:http://blog.csdn.net/droyon/article/details/45701257
源码级分析Android系统启动流程:http://www.cnblogs.com/rocomp/p/5001639.html
Android 时间同步原理分析:http://zhengken.me/2016/09/26/the-principle-of-date-time-sync/#more
Android中的时间时区自动更新:http://www.ithao123.cn/content-1560712.html
Android中时间和时区的自动更新(NITZ ZONE):http://www.itdadao.com/articles/c15a273865p0.html
“`
- Android时区和时间同步
- android 更换时区时间同步
- 设置linux时区和同步时间
- centos7时间同步和时区设置
- 修改LINUX时区和同步时间
- android-获取网络时间、获取特定时区时间、时间同步
- 定时同步时区时间
- 调整linux系统时间和时区 与Internet时间同步
- Linux 修改时间,时区和设置时间自动同步
- 调整linux系统时间和时区与Internet时间同步
- Android设置系统时间和时区
- Android设置系统时间和时区
- Android时间时区设置和获取
- ubuntu之设置时区和在线同步时间
- ubuntu之设置时区和在线同步时间
- Android 同步服务器时区
- linux系统时间同步,硬件时钟和系统时间同步,时区的设置
- android-获取网络时间、获取特定时区时间、时间同步的方法
- javascript 清除字符串空格
- Mac下安装pip报错
- 用FTP传输下载诊断事件与相关动作
- Linux下OpenCV的环境搭建
- MVC
- Android时区和时间同步
- python tkinter label编写案例分析
- mtklog结构及分析
- 基于Linux的SOCKET编程之TCP半双工Client-Server聊天程序
- 自学iOS开发系列----OC(类别和扩展)
- JDBC概述
- WINDOWS HOST文件修改后不生效的处理方法
- javascript 实现MD5加密
- Linux节点理解