Android系統時間同步NetworkTimeUpdateService

Android系統時間更新有兩種方式:NITZ時間和NTP時間,NITZ由運營商提供,通過sim卡更新時間,NTP爲通過網絡,訪問服務器獲取時間,這裏主要介紹NTP的方式。

NetworkTimeUpdateService啓動

NetworkTimeUpdateService作爲Android系統服務,在SystemServer中啓動

private void startOtherServices() {
	......
	NetworkTimeUpdateService networkTimeUpdater = null;
	  if (!disableNetwork && !disableNetworkTime) {
                traceBeginAndSlog("StartNetworkTimeUpdateService");
                try {
                    networkTimeUpdater = new NetworkTimeUpdateService(context);
                    ServiceManager.addService("network_time_update_service", networkTimeUpdater);
                } catch (Throwable e) {
                    reportWtf("starting NetworkTimeUpdate service", e);
                }
                traceEnd();
            }
            ......
            final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
			......
			try {
                if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
            } catch (Throwable e) {
                reportWtf("Notifying NetworkTimeService running", e);
            }
            ......
}

SystemServer中創建了NetworkTimeUpdateService,並註冊到ServiceManager以供client端調用,接着調用NetworkTimeUpdateService的systemRunning方法

我們先看NetworkTimeUpdateService構造方法

public NetworkTimeUpdateService(Context context) {
        mContext = context;
        //獲取NtpTrustedTime實例
        mTime = NtpTrustedTime.getInstance(context);
        //創建AlarmManager
        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
        //創建PendingPollIntent以供後面AlarmManager定時設置
        Intent pollIntent = new Intent(ACTION_POLL, null);
        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
		//mPollingIntervalMs代表請求ntp服務器的頻率
		//從config配置文件讀取,86400000ms,就是一天
        mPollingIntervalMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpPollingInterval);
        //mPollingIntervalShorterMs代表請求ntp服務器失敗後再次
        //請求的時間間隔,爲60000ms,就是1分鐘
        mPollingIntervalShorterMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpPollingIntervalShorter);
         //mTryAgainTimesMax 代表請求ntp服務器失敗後再次嘗試的
         //最大次數,爲3次
        mTryAgainTimesMax = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpRetry);
         //mTimeErrorThresholdMs代表請求ntp服務器得到的時間
         //和當前時間差大於此閾值,則更新時間。
        mTimeErrorThresholdMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpThreshold);
        mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
                PowerManager.PARTIAL_WAKE_LOCK, TAG);
    }

NetworkTimeUpdateService構造函數中初始化了各種值

NtpTrustedTime

NtpTrustedTime通過單例獲取,主要看下ntp服務器

    public static synchronized NtpTrustedTime getInstance(Context context) {
        if (sSingleton == null) {
            final Resources res = context.getResources();
            final ContentResolver resolver = context.getContentResolver();
            //默認ntp服務器,從配置文件讀取,time.android.com
            //這是google的服務器,國內用戶如果請求這個服務器可能無法更新時間
            final String defaultServer = res.getString(
                    com.android.internal.R.string.config_ntpServer);
            //timeout爲5s
            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);
            //默認都會使用配置文件中配置的ntp服務器
            final String server = secureServer != null ? secureServer : defaultServer;
            sSingleton = new NtpTrustedTime(server, timeout);
            sContext = context;
        }

        return sSingleton;
    }

systemRunning

接着看NetworkTimeUpdateService初始化完成後調用了systemRunning方法

public void systemRunning() {
		//註冊telephony的廣播
        registerForTelephonyIntents();
        //註冊Alarm發送的廣播
        registerForAlarms();
        //註冊網絡改變的廣播
        registerForConnectivityIntents();

        HandlerThread thread = new HandlerThread(TAG);
        thread.start();
        mHandler = new MyHandler(thread.getLooper());
        //發送EVENT_POLL_NETWORK_TIME消息
        mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
        /*
        創建SettingsObserver,監聽Settings中AUTO_TIME
        值的變化
        */
        mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
        mSettingsObserver.observe(mContext);
    }
    /*
    TelephonyIntents.ACTION_NETWORK_SET_TIME 和 
    TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE 廣播來監聽來自運營商
    的時間改變
    */
 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);
    }
    /*
    註冊自定義的廣播,ACTION_POLL由Alarm定時器來發送,
    在NetworkTimeUpdateService構造方法中創建了mPendingPollIntent
    供Alarm發送
    */
 private static final String ACTION_POLL =
            "com.android.server.NetworkTimeUpdateService.action.POLL";
 private void registerForAlarms() {
        mContext.registerReceiver(
            new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
                }
            }, new IntentFilter(ACTION_POLL));
    }
    /*
    監聽網絡發生變化的廣播
    */
private void registerForConnectivityIntents() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        mContext.registerReceiver(mConnectivityReceiver, intentFilter);
    }

SettingsObserver

 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();
            //監聽AUTO_TIME的值,也就是Settings中自動更新網絡時間的開關
            resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
                    false, this);
        }

        @Override
        public void onChange(boolean selfChange) {
            //當AUTO_TIME變化時發送EVENT_POLL_NETWORK_TIME消息
            mHandler.obtainMessage(mMsg).sendToTarget();
        }
    }

看一下mHandler,其實下面三個消息都會觸發請求ntp服務器的方法,下面來挨着看下這三個消息的發送條件

 private static final int EVENT_AUTO_TIME_CHANGED = 1;
 private static final int EVENT_POLL_NETWORK_TIME = 2;
 private static final int EVENT_NETWORK_CHANGED = 3;
 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_CHANGED:
                    onPollNetworkTime(msg.what);
                    break;
            }
        }
    }

EVENT_AUTO_TIME_CHANGED

這個就是在SettingsObserver的onChange方法裏發送的,當我們更新Settings中的AUTO_TIME開光就會觸發onPollNetworkTime更新時間,但是具體是否更新時間還要看onPollNetworkTime的一些判斷,等下再說

EVENT_POLL_NETWORK_TIME

這個消息在systemRunning方法和Alarms中發送,systemRunning我們知道在開機啓動的時候調用,Alarms呢,Alarms其實是在onPollNetworkTimeUnderWakeLock方法中設置的,等下再說

EVENT_NETWORK_CHANGED

這個比較簡單,就是監聽到網絡發生變化之後發送的

private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
                if (DBG) Log.d(TAG, "Received CONNECTIVITY_ACTION ");
                // Don't bother checking if we have connectivity, NtpTrustedTime does that for us.
                Message message = mHandler.obtainMessage(EVENT_NETWORK_CHANGED);
                // Send with a short delay to make sure the network is ready for use
                mHandler.sendMessageDelayed(message, NETWORK_CHANGE_EVENT_DELAY_MS);
            }
        }
    };

onPollNetworkTime

看了三種觸發onPollNetworkTime的條件,接着看onPollNetworkTime這個方法具體實現

private void onPollNetworkTime(int event) {
        // 如果沒有開啓自動更新時間直接return
        if (!isAutomaticTimeRequested()) return;
        mWakeLock.acquire();
        try {
            onPollNetworkTimeUnderWakeLock(event);
        } finally {
            mWakeLock.release();
        }
    }

onPollNetworkTimeUnderWakeLock

 private void onPollNetworkTimeUnderWakeLock(int event) {
        //從開機到現在的時間
        final long refTime = SystemClock.elapsedRealtime();
        //如果已經通過運營商更新了時間並且上次更新的時間到當前爲止
        //小於1天則設置一個Alarm定時器(定時器爲1天后觸發)並且return
        if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
            resetAlarm(mPollingIntervalMs);
            return;
        }
        final long currentTime = System.currentTimeMillis();
        // 如果沒有設置過ntp時間或者開機但現在的時間大於等於最後一次請求ntp
        //服務器加上1天的時間或者時因爲Settings的自動更新開關觸發的更新時間
        if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
                || event == EVENT_AUTO_TIME_CHANGED) {
            if (DBG) Log.d(TAG, "Before Ntp fetch");

            // mTime.getCacheAge等於上次請求ntp服務器的時間
            //如果大於等於1天則強制更新時間
            if (mTime.getCacheAge() >= mPollingIntervalMs) {
                mTime.forceRefresh();
            }

            // 如果上次請求ntp服務器的時間小於1天
            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.
                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
                    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 {
               //如果上次請求ntp服務器的時間已經大於1天了
                mTryAgainCounter++;
                //最多嘗試次數爲mTryAgainTimesMax,3次
                if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
                    //嘗試請求ntp服務器的時間間隔爲mPollingIntervalShorterMs,1分鐘
                    resetAlarm(mPollingIntervalShorterMs);
                } else {
                    // 如果三次請求之後則需要在1天之後再請求ntp服務器
                    mTryAgainCounter = 0;
                    resetAlarm(mPollingIntervalMs);
                }
                return;
            }
        }
        //保證無論如何都可以設置一個1天后請求ntp服務器的定時器
        resetAlarm(mPollingIntervalMs);
    }
    private void resetAlarm(long interval) {
        mAlarmManager.cancel(mPendingPollIntent);
        long now = SystemClock.elapsedRealtime();
        long next = now + interval;
        //在next時間之後執行mPendingPollIntent,mPendingPollIntent在
        //NetworkTimeUpdateService構造方法中初始化,作用時發送
        //自定義的ACTION_POLL廣播
        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
    }

mTime.forceRefresh()

NtpTrustedTime中的forceRefresh方法強制刷新時間

 @Override
    public boolean forceRefresh() {
       //如果ntp服務器爲空則返回
        if (TextUtils.isEmpty(mServer)) {
            // missing server, so no trusted time available
            return false;
        }
        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();
        //請求mServer,也就是time.android.com,5s超時
        if (client.requestTime(mServer, (int) mTimeout)) {
            mHasCache = true;
            //請求成功之後獲取的ntp時間
            mCachedNtpTime = client.getNtpTime();
            mCachedNtpElapsedRealtime = client.getNtpTimeReference();
            mCachedNtpCertainty = client.getRoundTripTime() / 2;
            return true;
        } else {
            return false;
        }
    }

client.requestTime

SntpClient中的requestTime方法發送socket請求獲取服務器時間

public boolean requestTime(InetAddress address, int port, int timeout) {
        DatagramSocket socket = null;
        final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NTP);
        try {
            socket = new DatagramSocket();
            socket.setSoTimeout(timeout);
            byte[] buffer = new byte[NTP_PACKET_SIZE];
            DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, 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
            final long requestTime = System.currentTimeMillis();
            final 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);
            final long responseTicks = SystemClock.elapsedRealtime();
            final long responseTime = requestTime + (responseTicks - requestTicks);

            // extract the results
            final byte leap = (byte) ((buffer[0] >> 6) & 0x3);
            final byte mode = (byte) (buffer[0] & 0x7);
            final int stratum = (int) (buffer[1] & 0xff);
            final long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);
            final long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
            final long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);

            /* do sanity check according to RFC */
            // TODO: validate originateTime == requestTime.
            checkValidServerReply(leap, mode, stratum, transmitTime);

            long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime);
            long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2;
            EventLogTags.writeNtpSuccess(address.toString(), roundTripTime, clockOffset);
            if (DBG) {
                Log.d(TAG, "round trip: " + roundTripTime + "ms, " +
                        "clock offset: " + clockOffset + "ms");
            }
            mNtpTime = responseTime + clockOffset;
            mNtpTimeReference = responseTicks;
            mRoundTripTime = roundTripTime;
        } catch (Exception e) {
            EventLogTags.writeNtpFailure(address.toString(), e.toString());
            if (DBG) Log.d(TAG, "request time failed: " + e);
            return false;
        } finally {
            if (socket != null) {
                socket.close();
            }
            TrafficStats.setThreadStatsTag(oldTag);
        }

        return true;
    }

請求完成之後獲取的時間如何更新呢,回到NetworkTimeUpdateService,
mCachedNtpTime = client.getNtpTime();

private void onPollNetworkTimeUnderWakeLock(int event) {
		......
		 
		  if (mTime.getCacheAge() >= mPollingIntervalMs) {
                mTime.forceRefresh();
            }
            //強制刷新時間之後
            if (mTime.getCacheAge() < mPollingIntervalMs) {
               //ntp請求服務器之後獲取的時間
                final long ntp = mTime.currentTimeMillis();
                mTryAgainCounter = 0;
               
                if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
                        || mLastNtpFetchTime == NOT_SET) {
                    // Set the system time
                    if (DBG && mLastNtpFetchTime == NOT_SET
                            && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
                    //保證獲取的網絡時間不會超過最大值
                    if (ntp / 1000 < Integer.MAX_VALUE) {
                       //設置ntp時間到系統中
                        SystemClock.setCurrentTimeMillis(ntp);
                    }
                } else {
                    if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
                }
                mLastNtpFetchTime = SystemClock.elapsedRealtime();
            }
		......
}

總結:觸發onPollNetworkTimeUnderWakeLock的三種方式
1.Settings中的自動更新時間開關變化
2.Alarm定時器中發送自定義廣播
3.網絡發生變化
至於最終是否需要請求ntp服務器更新網絡時間則需要一些判斷,最主要的就是上次請求ntp服務器的時間間隔與1天對比,如果請求ntp失敗時候又三次重試機會,如果再失敗則必須等待一天後再請求

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章