Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 時間更新機制研究

Android 時間更新機制研究

編輯:關於Android編程

最近因工作的需要,開始接觸到Android系統時間網絡同步更新的問題。遇到的實際問題如下:1、手機恢復出廠設置後,系統時間沒有及時更新。2、手機使用當中時間同步更新後,時間快了幾分鐘。3、手機狀態欄的時間的分鐘顯示沒有及時更新等。
鑒於各個項目問題的重復出現,有很多地方不是太明白,導致解決問題的效率比較低,正想研究一下,所以根據網上相關的資源介紹,對照Android 6.0的源碼進行分析,寫個文檔記錄一下。

一、概述
現在android通過網絡同步時間有兩種方式:一種是走運營商協議的NITZ,另一種是走網絡時鐘的NTP;
NITZ和NTP,它們使用的條件不同,可以獲取的信息也不一樣;勾選這個功能後,手機首先會嘗試NITZ方式,若獲取時間失敗,則使用NTP方式。
1.NITZ(network identity and time zone)同步時間
NITZ是一種GSM/WCDMA基地台方式,必須插入SIM卡,且需要operator支持;可以提供時間和時區信息

2.NTP(network time protocol)同步時間
NTP在無SIM卡或operator不支持NITZ時使用,單純通過網絡(GPRS/WIFI)獲取時間,只提供時間信息,沒有時區信息(因此在不支持NITZ的地區,自動獲取時區功能實際上是無效的)NTP還有一種緩存機制:當前成功獲取的時間會保存下來,當用戶下次開啟自動更新時間功能時會結合手機clock來進行時間更新。這也是沒有任何網絡時手機卻能自動更新時間的原因。此外,因為NTP是通過對時的server獲取時間,當同步時間失敗時,可以檢查一下對時的server是否有效,並替換為其他server試一下。

3.如何判斷手機通過哪種方式更新時間
設置一個錯誤的時區,查看時區是否有被更新正確,若時間和時區都有更新正確,那麼就是GSM網路有送NITZ消息上來;
若只有時間更新,而時區沒有變化,就是NTP方式,即它通過網絡(GPRS/WIFI)連接到server去獲取時間。

二、源碼目錄位置列表

frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
frameworks/opt/telephony/src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
frameworks/base/core/java/android/app/AlarmManager.java
frameworks/base/services/core/java/com/android/server/AlarmManagerService.java
frameworks/base/core/java/android/net/SntpClient.java
frameworks/base/core/java/android/util/NtpTrustedTime.java

三、Android自動更新時間源碼分析
1、首先選中自動更新。設置》高級設置》日期和時間》(自動確定日期和時間、自動確定時區)從Android4.0開始時間和時區是否自動更新可以分開設置。我們以時間自動更新為例:

源碼路徑:/packages_mtk_6750_mp/apps/Settings/src/com/android/settings/DateTimeSettings.java

    public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
        if (key.equals(KEY_AUTO_TIME)) {
            // /M: modify as MTK add GPS time Sync feature @{
            String value = mAutoTimePref.getValue();
            int index = mAutoTimePref.findIndexOfValue(value);
            mAutoTimePref.setSummary(value);
            boolean autoEnabled = true;
            if (index == AUTO_TIME_NETWORK_INDEX) {
                Settings.Global.putInt(getContentResolver(),
                        Settings.Global.AUTO_TIME, 1);
                Settings.Global.putInt(getContentResolver(),
                        Settings.System.AUTO_TIME_GPS, 0);
            } else if (index == AUTO_TIME_GPS_INDEX) {
                showDialog(DIALOG_GPS_CONFIRM);
                setOnCancelListener(this);
            } else {
                Settings.Global.putInt(getContentResolver(), Settings.Global.AUTO_TIME, 0);
                Settings.Global.putInt(getContentResolver(), Settings.System.AUTO_TIME_GPS, 0);
                autoEnabled = false;
            }
            // /@}
            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);
        }
    }
//注釋:這是一個監聽,當點擊自動checkbox後,會觸發此監聽事件,其中設置了Settings.Global.AUTO_TIME
//和Settings.Global.AUTO_TIME_ZONE的值,disable/enable了時間、日期、區域三個選擇項。

2、後台監聽處理:
見GsmServiceStateTracker.java

    public GsmServiceStateTracker(GSMPhone phone) {
        super(phone, phone.mCi, new CellInfoGsm());
        ......
        mCr.registerContentObserver(
                Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
                mAutoTimeObserver);
        mCr.registerContentObserver(
                Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
                mAutoTimeZoneObserver);
        ......
        }
    private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
        @Override
        public void onChange(boolean selfChange) {
            Rlog.i("GsmServiceStateTracker", "Auto time state changed");
            revertToNitzTime();
        }
    };

    private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) {
        @Override
        public void onChange(boolean selfChange) {
            Rlog.i("GsmServiceStateTracker", "Auto time zone state changed");
            revertToNitzTimeZone();
        }
    };

注釋:GSM方式和CDMA方式原理是一樣的,只是不同的運營商的支持不同而已,接下來的分析我們以CDMA為例。
見CdmaServiceStateTracker.java

//設置Settings.Global.AUTO_TIME監聽
protected CdmaServiceStateTracker(CDMAPhone phone, CellInfo cellInfo) {
        super(phone, phone.mCi, cellInfo);
        ......
        mCr.registerContentObserver(
                Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
                mAutoTimeObserver);
        mCr.registerContentObserver(
            Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
            mAutoTimeZoneObserver);
        ......
}
//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();
        }
    };

    private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) {
        @Override
        public void onChange(boolean selfChange) {
            if (DBG) log("Auto time zone state changed");
            revertToNitzTimeZone();
        }
    };

    private void revertToNitzTime() {
        if (Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME, 0) == 0) {
            return;
        }
        if (DBG) {
            log("revertToNitzTime: mSavedTime=" + mSavedTime + " mSavedAtTime=" + mSavedAtTime);
        }
        //Settings.Global.AUTO_TIME真正的處理函數,其取得從gsm/cdma取得的時間參數,
        //對系統時間已經區域進行更新,並發送廣播消息。
        if (mSavedTime != 0 && mSavedAtTime != 0) {
            setAndBroadcastNetworkSetTime(mSavedTime
                    + (SystemClock.elapsedRealtime() - mSavedAtTime));
        }
    }
//這個函數做的事情比較簡單,就是判斷了下有沒有選中自動更新,沒有,就返回。有,那再繼續判斷,
//mSavedTime和mSavedAtTime為不為0(這兩個變量後面再講),都不為0,那麼發送廣播。

    private void setAndBroadcastNetworkSetTime(long time) {
        if (DBG) log("setAndBroadcastNetworkSetTime: time=" + time + "ms");
        /*此處進行了一次系統時間的設置,這裡簡單介紹一下流程:
        通過SystemClock類的setCurrentTimeMillis方法設置時間,
        在SystemClock類中拿到AlarmManagerService的代理端AlarmManager的引用,
        通過Binder機制將值傳至AlarmManagerService,再通過JNI傳至底層。*/
        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);
    }
//發送廣播如上,廣播接收的地方是在NetworkTimeUpdateService.java

注釋:以上是從設置裡面點擊觸發一次時間同步,那麼系統又是如何自動進行時間同步的呢,當選擇自動設置以後
系統時間同步,是靠ril層(見Reference-ril.c)發消息RIL_UNSOL_NITZ_TIME_RECEIVED來進行驅動的,這裡就不詳細介紹此消息是如何發生的了,其主要由gsm/cdma模塊來進行驅動的,想更深入的了了解,需要對RIL、MUX、AT、運營商協議 以及代碼進行綜合的分析,在這裡就不做詳細介紹。

當Ril.java接收到RIL_UNSOL_NITZ_TIME_RECEIVED消息,會將消息轉發為EVENT_NITZ_TIME,
同時在CdmaServiceStateTracker中handleMessage會接收到EVENT_NITZ_TIME消息了並進行相關處理,詳細代碼見下:

    public void handleMessage (Message msg) {
        AsyncResult ar;
        int[] ints;
        String[] strings;

        if (!mPhone.mIsTheCurrentActivePhone) {
            loge("Received message " + msg + "[" + msg.what + "]" +
                    " while being destroyed. Ignoring.");
            return;
        }

        switch (msg.what) {
        ......
        case EVENT_NITZ_TIME:
          ar = (AsyncResult) msg.obj;

            String nitzString = (String)((Object[])ar.result)[0];
            long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue();
            //EVENT_NITZ_TIME消息處理,獲取gsm/cdma發送過來的時間參數,
            //並且調用setTimeFromNITZString進行處理
            setTimeFromNITZString(nitzString, nitzReceiveTime);
            break;
        ......
        }
    }

    private
    void setTimeFromNITZString (String nitz, long nitzReceiveTime)
    {
        // "yy/mm/dd,hh:mm:ss(+/-)tz"
        // tz is in number of quarter-hours

        long start = SystemClock.elapsedRealtime();
        ......
        if (getAutoTime()) {
            /**
             * Update system time automatically
             */
            long gained = c.getTimeInMillis() - System.currentTimeMillis();
            long timeSinceLastUpdate = SystemClock.elapsedRealtime() - mSavedAtTime;
            int nitzUpdateSpacing = Settings.Global.getInt(mCr,
                    Settings.Global.NITZ_UPDATE_SPACING, mNitzUpdateSpacing);
            int nitzUpdateDiff = Settings.Global.getInt(mCr,
                    Settings.Global.NITZ_UPDATE_DIFF, mNitzUpdateDiff);

            if ((mSavedAtTime == 0) || (timeSinceLastUpdate > nitzUpdateSpacing)
                    || (Math.abs(gained) > nitzUpdateDiff)) {
                if (DBG) {
                    log("NITZ: Auto updating time of day to " + c.getTime()
                        + " NITZ receive delay=" + millisSinceNitzReceived
                        + "ms gained=" + gained + "ms from " + nitz);
                }
                setAndBroadcastNetworkSetTime(c.getTimeInMillis());
            } else {
                if (DBG) {
                    log("NITZ: ignore, a previous update was "
                        + timeSinceLastUpdate + "ms ago and gained=" + gained + "ms");
                }
                return;
            }
        }
        ......
    }

    private boolean getAutoTime() {
        try {
            return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME) > 0;
        } catch (SettingNotFoundException snfe) {
            return true;
        }
    }

setTimeFromNITZString時間更新處理函數,當自動選擇框為true,當獲取的時間值和系統的時間值大於一定時,
調用setAndBroadcastNetworkSetTime(c.getTimeInMillis())對系統時間進行更新,
並且發送廣播消息TelephonyIntents.ACTION_NETWORK_SET_TIME。此廣播消息被NetWorkTimeupdataService接收到,
並且進行了相關的時間同步處理。接下來看NetworkTimeUpdateService.java

    private void registerForTelephonyIntents() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
        intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
        mContext.registerReceiver(mNitzReceiver, intentFilter);
    }
    //前面講到廣播是從CdmaServiceStateTracker.java中發出,找到它的receiver,函數只是賦值了兩個變量。
    /** 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)) {
                if (DBG) Log.d(TAG, "mNitzReceiver Receive ACTION_NETWORK_SET_TIME");
                mNitzTimeSetTime = SystemClock.elapsedRealtime();
            } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
                if (DBG) Log.d(TAG, "mNitzReceiver Receive ACTION_NETWORK_SET_TIMEZONE");
                mNitzZoneSetTime = SystemClock.elapsedRealtime();
            }
        }
    };

真正更新時間的地方在哪兒?
還是在NetworkTimeupdateService.java中,它也注冊了ContentObserver。

    /** 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();
        }
    }

    /** Initialize the receivers and initiate the first NTP request */
    //開機後,會調用該類的systemRunning方法
    public void systemRunning() {
        //registerForTelephonyIntents該方法,注冊監聽來自Telephony Ril相關的廣播。
        registerForTelephonyIntents();
        //registerForAlarms此方法,是配合構造函數中的mPendingPollIntent 來工作的,
        //主要作用是構造handler Message並再次發起時間同步請求。
        registerForAlarms();
        //此方法監聽移動數據連接,移動網絡連接後,收到信息,發起時間同步請求。
        registerForConnectivityIntents();
        //構建Message,發起時間同步請求。
        HandlerThread thread = new HandlerThread(TAG);
        thread.start();
        mHandler = new MyHandler(thread.getLooper());
        // Check the network time on the new thread
        mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
        //構建監聽數據庫的Observer,監聽來自設置等發起的時間同步請求。
        //在SettingsObserver中構建handler Message請求,發起時間同步。
        mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
        mSettingsObserver.observe(mContext);
        ......
    }
    //上面我們講到了接收的來自Telephony相關的廣播,或者數據庫變化,
    //我們都會發送Message給Handler,我們的handler是如下處理這些請求的:
    /** 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) {
                //接收請求類型:EVENT_AUTO_TIME_CHANGED、EVENT_POLL_NETWORK_TIME、
                //EVENT_NETWORK_CONNECTED,這些請求邏輯,我們都會發起onPollNetworkTime
                //來進行相關邏輯處理。也就是說,onPollNetworkTime方法就是我們時間同步的主要關注對象。
                case EVENT_AUTO_TIME_CHANGED:
                case EVENT_POLL_NETWORK_TIME:
                case EVENT_NETWORK_CHANGED:
                    if (DBG) Log.d(TAG, "MyHandler::handleMessage what = " + msg.what);
                    onPollNetworkTime(msg.what);
                    break;

                /// M: comment @{ add GPS Time Sync Service
                case EVENT_GPS_TIME_SYNC_CHANGED:
                    boolean gpsTimeSyncStatus = getGpsTimeSyncState();;
                    Log.d(TAG, "GPS Time sync is changed to " + gpsTimeSyncStatus);
                    onGpsTimeChanged(gpsTimeSyncStatus);
                    break;
                /// @}
            }
        }
    }

SettingsObserver就是一個ContentObserver,以上是具體的代碼,很簡單。
好的,繼續分析更改時間的地方,找到handleMessage裡的回調函數,onPollNetworkTime(int event)。

在分析該函數之前我先簡單介紹幾個變量:
配置文件路徑:frameworks/base/core/res/res/values/config.xml

    asia.pool.ntp.org    
    
    86400000
    
    10000
    
    4
    
    5000
    
    20000
    // 正常輪詢的頻率 該值為86400000ms即24小時。多次嘗試同步時間無果,24小時會再次發起時間同步請求
    private final long mPollingIntervalMs;
    // 網絡請求失敗時,再次請求輪詢的間隔 10000ms即10秒。時間同步超時,再次發起時間同步請求。
    private final long mPollingIntervalShorterMs;
    // 再次輪詢的最大次數 4
    private final int mTryAgainTimesMax;
    // 如果時間差大於門檻值5000ms即5秒,則更新時間
    private final int mTimeErrorThresholdMs;
    // 該值記錄輪詢的次數
    private int mTryAgainCounter;
    //時間同步服務器。此處可以多增加幾個時間同步服務器,大陸、美國、台灣等多梯度配置。
    private static final String[] SERVERLIST =  new String[]{
                                             "1.cn.pool.ntp.org",
                                             "2.cn.pool.ntp.org",
                                             "3.cn.pool.ntp.org",
                                             "0.cn.pool.ntp.org"
                                             };
    ......
    public NetworkTimeUpdateService(Context context) {
        mContext = context;
        //初始化NtpTrustedTime對象。
        mTime = NtpTrustedTime.getInstance(context);
        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
        Intent pollIntent = new Intent(ACTION_POLL, null);
        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);

        mPollingIntervalMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpPollingInterval);
        mPollingIntervalShorterMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpPollingIntervalShorter);
        mTryAgainTimesMax = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpRetry);
        mTimeErrorThresholdMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpThreshold);
        //設置默認NTP服務器
        mDefaultServer = ((NtpTrustedTime) mTime).getServer();
        mNtpServers.add(mDefaultServer);
        for (String str : SERVERLIST)
        {
           mNtpServers.add(str);
        }
        mTryAgainCounter = 0;
    }

好了,了解了這些變量後,我們再回到回調函數onPollNetworkTime()。

private void onPollNetworkTime(int event) {
   if (DBG) Log.d(TAG, "onPollNetworkTime start");
   //1、是否勾選自動同步時間配置
   // If Automatic time is not set, don't bother.
   if (!isAutomaticTimeRequested()) return;
   if (DBG) Log.d(TAG, "isAutomaticTimeRequested() = True");

   final long refTime = SystemClock.elapsedRealtime();
   // If NITZ time was received less than mPollingIntervalMs time ago,
   // no need to sync to NTP.
   if (DBG) Log.d(TAG, "mNitzTimeSetTime: " + mNitzTimeSetTime + ",refTime: " + refTime);
   //2、mNitzTimeSetTime 來自Moderm,如果當前時間剛通過moderm更新不久,則不進行時間同步。
   if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
       resetAlarm(mPollingIntervalMs);
       return;
   }
   //3、如果機器剛啟動,或者機器運行時間大於mPollingIntervalMs,即24小時,
   //或者設置等發起的主動更新時間請求,則發起網絡時間同步請求。否則,24小時後再進行時間同步。
   final long currentTime = System.currentTimeMillis();
   if (DBG) Log.d(TAG, "System time = " + currentTime);
   // Get the NTP time
   if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
           || event == EVENT_AUTO_TIME_CHANGED) {
       if (DBG) Log.d(TAG, "Before Ntp fetch");
        //3.1、是否含有時間緩沖,如無,發起時間同步,
       // force refresh NTP cache when outdated
       if (mTime.getCacheAge() >= mPollingIntervalMs) {
           //M: For multiple NTP server retry
           //mTime.forceRefresh();
           int index = mTryAgainCounter % mNtpServers.size();
           if (DBG) Log.d(TAG, "mTryAgainCounter = " + mTryAgainCounter
               + ";mNtpServers.size() = " + mNtpServers.size()
               + ";index = " + index + ";mNtpServers = " + mNtpServers.get(index));
           //3.1.1、遍歷時間服務器,發起時間同步
           if (mTime instanceof NtpTrustedTime)
           {
               ((NtpTrustedTime) mTime).setServer(mNtpServers.get(index));
               mTime.forceRefresh();
               ((NtpTrustedTime) mTime).setServer(mDefaultServer);
           }
           else
           {
               mTime.forceRefresh();
           }
       }
        //3.2、獲取最新同步的時間緩沖數據,如無,則再次發起時間同步,
        //間隔時間為mPollingIntervalShorterMs,即30秒。
       // only update when NTP time is fresh
       if (mTime.getCacheAge() < mPollingIntervalMs) {
           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.
           //3.2.1、如果開機第一次同步或者最新時間與當前時間差別超過mTimeErrorThresholdMs即5秒,
           //則進行時間設定。否則認定新同步時間與當前時間差別不大,不覆蓋當前時間。
           if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
                   || mLastNtpFetchTime == NOT_SET) {
               // Set the system time
               if (DBG && mLastNtpFetchTime == NOT_SET
                       && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
                   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
               //3.2.2、設定同步時間
               if (ntp / 1000 < Integer.MAX_VALUE) {
                   /*關於設置系統時間流程在CdmaServiceStateTracker類的
                   setAndBroadcastNetworkSetTime方法中有做過簡單介紹。*/
                   SystemClock.setCurrentTimeMillis(ntp);
               }
           } else {
               if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
           }
           Amigo_notifyStatus(ntp, currentTime);       
           mLastNtpFetchTime = SystemClock.elapsedRealtime();
       } else {
           Log.d(TAG, "fail : mTryAgainCounter  " + mTryAgainCounter);

           if (isNetworkConnected()) {
               if (DBG) Log.d(TAG, "isNetworkConnected() = true");
               //3.3 如果不大於最大同步次數,10秒後進行時間同步,否則,24小時後更新。
               mTryAgainCounter++;
               if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
                   // Try again shortly
                   resetAlarm(mPollingIntervalShorterMs);
               } else {
                   // Try much later
                   if (DBG) Log.d(TAG, "mNitzTimeSetTime: " + mNitzTimeSetTime + ",refTime: " + refTime);
                   if (mNitzTimeSetTime == NOT_SET || refTime - mNitzTimeSetTime >= mPollingIntervalMs) {
                       Amigo_notifyStatusFalil();
                   }
                   mTryAgainCounter = 0;
                   resetAlarm(mPollingIntervalMs);
               }
           } else {
               if (DBG) Log.d(TAG, "isNetworkConnected() = false");
               mTryAgainCounter = 0;
               resetAlarm(mPollingIntervalMs);
           }

           return;
       }
   }
   //4、如果剛更新時間不久,則24小時後再發起時間同步請求。
   resetAlarm(mPollingIntervalMs);
}

以上介紹了時間獲取的相關邏輯,我們接下來看下時間是如何發起同步的,這個方法的主角為:NtpTrustedTime
在該類中通過forceRefresh方法來更新獲取服務器時間。

    public boolean forceRefresh() {
        if (TextUtils.isEmpty(mServer)) {
            // missing server, so no trusted time available
            return false;
        }

        // We can't do this at initialization time: ConnectivityService might not be running yet.
        synchronized (this) {
            if (mCM == null) {
                mCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
            }
        }

        final NetworkInfo ni = mCM == null ? null : mCM.getActiveNetworkInfo();
        if (ni == null || !ni.isConnected()) {
            if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");
            return false;
        }

        if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
        final SntpClient client = new SntpClient();
        if (client.requestTime(mServer, (int) mTimeout)) {
            mHasCache = true;
            mCachedNtpTime = client.getNtpTime();
            mCachedNtpElapsedRealtime = client.getNtpTimeReference();
            mCachedNtpCertainty = client.getRoundTripTime() / 2;
            return true;
        } else {
            return false;
        }
    }

在該方法邏輯中,通過SntpClient來封裝請求。
我們傳入在NetworkTimeUpdateService傳入的服務器地址以及請求超時時間(20秒,見配置文件config_ntpTimeout),
向host服務器發起請求,並將相應結果按照編解碼規則封裝進二進制數組。見SntpClient.java

    public boolean requestTime(String host, int timeout) {
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket();
            socket.setSoTimeout(timeout);
            InetAddress address = InetAddress.getByName(host);
            byte[] buffer = new byte[NTP_PACKET_SIZE];
            DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT);

            // set mode = 3 (client) and version = 3
            // mode is in low 3 bits of first byte
            // version is in bits 3-5 of first byte
            buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);

            // get current time and write it to the request packet
            long requestTime = System.currentTimeMillis();
            long requestTicks = SystemClock.elapsedRealtime();
            writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);

            socket.send(request);

            // read the response
            DatagramPacket response = new DatagramPacket(buffer, buffer.length);
            socket.receive(response);
            long responseTicks = SystemClock.elapsedRealtime();
            long responseTime = requestTime + (responseTicks - requestTicks);

            // extract the results
            long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);
            long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
            long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
            long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime);
            // receiveTime = originateTime + transit + skew
            // responseTime = transmitTime + transit - skew
            // clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2
            //             = ((originateTime + transit + skew - originateTime) +
            //                (transmitTime - (transmitTime + transit - skew)))/2
            //             = ((transit + skew) + (transmitTime - transmitTime - transit + skew))/2
            //             = (transit + skew - transit + skew)/2
            //             = (2 * skew)/2 = skew
            ///M: ALPS00657881 bug fixed @{
            long clockOffset = 0;

            if (originateTime <= 0) {
                Log.d(TAG, "originateTime: " + originateTime);
                clockOffset = ((receiveTime - requestTime) + (transmitTime - responseTime)) / 2;
            } else {
                clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime)) / 2;
            }
            ///@}
            // if (false) Log.d(TAG, "round trip: " + roundTripTime + " ms");
            // if (false) Log.d(TAG, "clock offset: " + clockOffset + " ms");

            // save our results - use the times on this side of the network latency
            // (response rather than request time)
            mNtpTime = responseTime + clockOffset;
            mNtpTimeReference = responseTicks;
            mRoundTripTime = roundTripTime;
        } catch (Exception e) {
            if (false) Log.d(TAG, "request time failed: " + e);
            return false;
        } finally {
            if (socket != null) {
                socket.close();
            }
        }

        return true;
    }

總結:NetworkTimeUpdateService時間同步,一旦發起成功的時間同步,時間數據會存在內存中,並根據當前機器運行時間來設定最新的時間。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved