Android 同步网络时间

来源:互联网 发布:高分少女 知乎: 编辑:程序博客网 时间:2024/05/18 11:11

分析这个功能点之前先了解一下相关概念:


网络时间协议(英语:Network Time Protocol,NTP)是以分组交换把两台电脑的时钟同步化的网络传输协议。NTP使用UDP端口123作为传输层。它是用作抵销可变延迟的影响。

NTP是仍在使用中的最古老的网络传输协议之一(在1985年前开始)。NTP最初由特拉华大学的Dave Mills 设计,他与一群志愿者仍在维护NTP。

NTP与更简单的DAYTIME(RFC 867)和TIME(RFC 868)网络传输协议没有关连。

android 系统在设置中有同步网络时间的功能。

这里写图片描述


Settings.System.putString(getContentResolver(),                Settings.System.TIME_12_24,                is24Hour? HOURS_24 : HOURS_12);

抽取设置中该代码片段,从代码片段来看仅仅是往数据库中存入了一个数值标识为要同步网络时间。具体怎么实现呢?
在源码中有NetworkTimeUpdateService.java这么一个类来负责这个事情,这个类监控这个值的变化。其中

    /** Initialize the receivers and initiate the first NTP request */    public void systemRunning() {        registerForTelephonyIntents();        registerForAlarms();        registerForConnectivityIntents();        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();        mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);        mSettingsObserver.observe(mContext);    }    /** 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_CHANGED:                    onPollNetworkTime(msg.what);                    break;            }        }    }    private void onPollNetworkTime(int event) {        // If Automatic time is not set, don't bother.        if (!isAutomaticTimeRequested()) return;        final long refTime = SystemClock.elapsedRealtime();        // If NITZ time was received less than mPollingIntervalMs time ago,        // no need to sync to NTP.        if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {            resetAlarm(mPollingIntervalMs);            return;        }        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");            // force refresh NTP cache when outdated            if (mTime.getCacheAge() >= mPollingIntervalMs) {                mTime.forceRefresh();            }            // 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.                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 {                // Try again shortly                mTryAgainCounter++;                if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {                    resetAlarm(mPollingIntervalShorterMs);                } else {                    // Try much later                    mTryAgainCounter = 0;                    resetAlarm(mPollingIntervalMs);                }                return;            }        }        resetAlarm(mPollingIntervalMs);    }

onPollNetworkTime就干了同步的事情。
其中 final long ntp = mTime.currentTimeMillis(); 就是获取网络时间。

private TrustedTime mTime; mTime = NtpTrustedTime.getInstance(context);

中NtpTrustedTime这类就起了同步的功能。其中

    @Override    public long currentTimeMillis() {        if (!mHasCache) {            throw new IllegalStateException("Missing authoritative time source");        }        if (LOGD) Log.d(TAG, "currentTimeMillis() cache hit");        // current time is age after the last ntp cache; callers who        // want fresh values will hit makeAuthoritative() first.        return mCachedNtpTime + getCacheAge();    }

中mCachedNtpTime

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;        }

通过requestTime获取的,这个

   /**     * Sends an SNTP request to the given host and processes the response.     *     * @param host host name of the server.     * @param timeout network timeout in milliseconds.     * @return true if the transaction was successful.     */    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            long 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;    }

其中这个host是在实例化NtpTrustedTime这个类的时候赋予的:

    public static synchronized NtpTrustedTime getInstance(Context context) {        if (sSingleton == null) {            final Resources res = context.getResources();            final ContentResolver resolver = context.getContentResolver();            final String defaultServer = res.getString(                    com.android.internal.R.string.config_ntpServer);            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);            final String server = secureServer != null ? secureServer : defaultServer;            sSingleton = new NtpTrustedTime(server, timeout);            sContext = context;        }        return sSingleton;    }

/frameworks/base/core/res/res/values/config.xml
其中com.android.internal.R.string.config_ntpServer 这个数值就是要访问的服务器地址。5.1.1源码中的值是:

<string translatable="false" name="config_ntpServer">2.android.pool.ntp.org</string>
0 3
原创粉丝点击