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
“`

0 0
原创粉丝点击