Android时区和时间同步

概念

NITZ(Network identifier Time Zone:网络标识和时区):用来配置本地日期和时间的机制,也通过无线网络向设备提供运营商信息。常用来更新设备的系统时钟,可校正时间和时区。需要运营商支持(如果不支持的话就需要采用SNTP更新时间了)
SNTP(Simple Network Time protocol:简单网络时间协议):用于使用Internet时间校正设备时间,只能校正时间,无法校正时区

系统启动和主动设置功能流程


Android时区和时间同步_第1张图片
Android时区和时间同步_第2张图片

系统开机时间设置原理

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请求,更新本地时间,否则以本地时间为准

主动设置“自动确定日期和时间”与“自动确定时区”原理

进入”设置“—”日期和时间“,界面如下:
Android时区和时间同步_第3张图片
我们先看看”自动确定日期和时间“的实现:
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
“`

你可能感兴趣的:(系统时间同步)