Android -- 系统网络时间更新NetworkTimeUpdateService服务解析

Android 时间同步机制及常见问题分析 分析Android 时间同步更新机制的构成及常见导致时间不准的情况 阅读详情

Android -- 系统网络时间更新NetworkTimeUpdateService服务解析


Android系统时间更新主要涉及到两种时间:NITZ时间和NTP时间。NITZ时间与手机服务运营商关系密切;而NTP网络时间则较为独立,访问某个服务器,获取时间即可。NTP时间获取的过程较为单一,且由于工作中主要是涉及到Android NTP时间的获取;所以这里先只介绍Android系统获取NTP时间的流程。


NTP其实是一种协议,Android系统中通过一个Socket向一个提供NTP服务的服务器发送请求来获取NTP时间。既然借助于Socket通信,那么功能实现的过程中就会涉及到服务器地址、请求超时时间等信息的配置。这部分我们在后续的分析过程中都会看到。


Android NTP时间更新主要由NetworkUpdateService服务处理,它决定何时更新NTP时间。在systemserver进程启动时,它会被初始化;同时,它是Binder的子类:

                    networkTimeUpdater = new NetworkTimeUpdateService(context);
                    ServiceManager.addService("network_time_update_service", networkTimeUpdater);
首先创建服务实例,接着将该服务发布到系统中。创建完服务对象,并当系统准备完成后,会直接启动该服务:
try {
                    if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
                } catch (Throwable e) {
                    reportWtf("Notifying NetworkTimeService running", e);
                }
手段就是调用NetworkTimeUpdateService::systemRunning()方法。通过这两步,时间更新服务就处于工作状态了。


那么我们通过分析这两个步骤,来理解NetworkTimeUpdateService的工作流程


一、创建NetworkTimeUpdateService


NetworkTimeUpdateService的构造函数实现如下:

    public NetworkTimeUpdateService(Context context) {
        mContext = context;
        mTime = NtpTrustedTime.getInstance(context);//初始化NtpTrustedTime实例,它负责NTP时间的获取
        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);//获取闹钟服务

		//该PendingIntent对象用于当某次Ntp时间获取失败后,开启一个定时服务,去再次请求时间
		Intent pollIntent = new Intent(ACTION_POLL, null);
        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);

		//初始化了几个成员变量,它们决定了Ntp时间更新的细节
        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);

        mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
                PowerManager.PARTIAL_WAKE_LOCK, TAG);
    }
首先初始化NtpTrustedTime类实例,它负责NTP时间获取的具体操作,而NetworkTimeUpdateService服务决定何时触发NTP时间请求。


NtpTrustedTime实例的构造过程如下,它采用了单例设计模式:

    private NtpTrustedTime(String server, long timeout) {
        if (LOGD) Log.d(TAG, "creating NtpTrustedTime using " + server);
        mServer = server;
        mTimeout = timeout;
    }

    public static synchronized NtpTrustedTime getInstance(Context context) {
        if (sSingleton == null) {
            final Resources res = context.getResources();
            final ContentResolver resolver = context.getContentResolver();

			 //读取用户配置的NTP服务器地址、超时信息等
            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;
    }

读取配置文件和数据库配置的Ntp服务器地址和请求超时时间等信息,然后创建NtpTrustedTime对象并返回。其中,涉及到的服务器地址和超时信息会在config.xml和数据库中配置。如这里的默认NTP请求地址配置为:

    <!-- Remote server that can provide NTP responses. -->
    <string translatable="false" name="config_ntpServer">2.android.pool.ntp.org</string>
NtpTrustedTime封装了具体请求NTP时间的接口,NetworkTimeUpdateService如果需要更新NTP时间,通过创建的mTime实例即可实现。


回到NetworkTimeUpdateService的构造函数,创建完NtpTrustedTime实例后,还会获取Alarm服务对象;因为,当某刻NTP请求失败,系统会定时地去尝试再次获取NTP时间。其中创建的mPendingPollIntent就代表我们的这种意图。随后,这里初始化了几个long型变量,它会在NTP时间请求过程中决定一些细节分支处理,所以我们要明确它们的具体含义:

		//初始化了三个成员变量,它们决定了Ntp时间更新的细节
        mPollingIntervalMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpPollingInterval);//86400000ms
        mPollingIntervalShorterMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpPollingIntervalShorter);//60000ms
        mTryAgainTimesMax = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpRetry);//3
        mTimeErrorThresholdMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpThreshold);//5000

  • mPollingIntervalMs:Normal polling frequency in milliseconds -> 每两次刷新请求之间的时间间隔;决定是否需要刷新NTP时间
  • mPollingIntervalShorterMs:Try-again polling interval in milliseconds, in case the network request failed -> 某次请求失败后,再次去请求的时间间隔
  • mTryAgainTimesMax:Number of times to try again with the shorter interval, before backing off until the normal polling interval. A value < 0 indicates infinite -> 最大的尝试请求次数
  • mTimeErrorThresholdMs:If the time difference is greater than this threshold in milliseconds, then update the time -> 当新旧时间差别大于该值时,更新时间

初始化了这些成员变量,NetworkTimeUpdateService的创建工作就完成了。接着就要去启动该服务了。


二、NetworkTimeUpdateService::systemRunning()


NetworkTimeUpdateService实例创建完成后,会调用systemRunning()启动它:

    /** 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);
    }
说是启动,其实主要的工作就是初始化了一些触发时间更新的条件。
    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);
    }

    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);
    }
    /** Receiver for Nitz time events */
    private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (DBG) Log.d(TAG, "Received " + action);
            if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
                mNitzTimeSetTime = SystemClock.elapsedRealtime();
            } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
                mNitzZoneSetTime = SystemClock.elapsedRealtime();
            }
        }
    };

    /** Receiver for ConnectivityManager events */
    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);
            }
        }
    };

三个register方法分别注册了该服务关心的一些广播,其中一些广播的处理会通过发送消息事件去触发时间更新流程。其中,网络连接更新变化是会触发时间更新的;这在MyHandler的处理中可以看到。


MyHandler是NetworkTimeUpdateService内处理这些引发时间更新的异步消息事件的类,它工作在单独的线程中:

    /** 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;
            }
        }
    }
MyHandler中处理了三个事件,这样表明只在三种情况下,会主动去触发时间更新流程:

  • 监听到Settings.Global.AUTO_TIME字段有变化,表明用户是否使用自动时间
  • 服务内部开启的时间更新定时服务,如某次时间更新失败,需要在下一次再次去触发
  • 网络连接状态发生变化,如网络连接状态从未连接到连接,此时就需要触发时间更新

SettingsObserver会监听Settings.Global.AUTO_TIME字段是否变化,它对应的就是上面介绍的第一种触发情况:

    /** 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();
        }
    }
当监听到Settings.Global.AUTO_TIME字段有变化时,会发送消息事件EVENT_AUTO_TIME_CHANGED给MyHandler;如果字段值设为了1,表明用户希望自动设置时间,那么此时就会去触发时间更新。

systemRunning()中还主动触发了一次时间更新操作:
       // Check the network time on the new thread
        mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
通过发送EVENT_POLL_NETWORK_TIME事件;三种情况下,时间更新的流程都由onPollNetworkTime()处理,分析一种情况就等于分析了三种情况。直接看它的处理过程:
    /**
     * Checks if the user prefers to automatically set the time.
     */
    private boolean isAutomaticTimeRequested() {
        return Settings.Global.getInt(
                mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
    }

    private void onPollNetworkTime(int event) {
        // If Automatic time is not set, don't bother.
        if (!isAutomaticTimeRequested()) return;
        mWakeLock.acquire();
        try {
            onPollNetworkTimeUnderWakeLock(event);
        } finally {
            mWakeLock.release();
        }
    }
首先判断Settings.Global.AUTO_TIME字段的值,如果为1,则去自动更新、设置时间。onPollNetworkTimeUnderWakeLock()函数处理如下:
    private void onPollNetworkTimeUnderWakeLock(int event) {
        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) {//NITZ时间部分,如果两次时间的差异小于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) {//如果我们是第一次设置时间,或者两次更新时间的间隔大于了mPollingIntervalMs值,或者当前触发更新的事件是EVENT_AUTO_TIME_CHANGED;则继续更新流程
            if (DBG) Log.d(TAG, "Before Ntp fetch");

            // force refresh NTP cache when outdated
            if (mTime.getCacheAge() >= mPollingIntervalMs) {//如果这次时间更新的时间节点和上一次时间更新时获取到结果的时间节点之间的差异大于mPollingIntervalMs时,认为时间过期,需要强制刷新时间;
                mTime.forceRefresh();
            }

            // only update when NTP time is fresh
            if (mTime.getCacheAge() < mPollingIntervalMs) {//这种情况下认为NTP时间是新鲜的、有效的
                final long ntp = mTime.currentTimeMillis();//获取当前的NTP时间
                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);//将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);//设置定时任务,隔一段时间后再去尝试刷新时间
    }
NTP时间的刷新动作是由NtpTrustedTime::forceRefresh()函数实现:
@Override
    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类来实现此时的NTP socket请求:

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封装了获取NTP时间的socket接口:

/**
     * 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) {
        InetAddress address = null;
        try {
            address = InetAddress.getByName(host);//
        } catch (Exception e) {
            if (DBG) Log.d(TAG, "request time failed: " + e);
            return false;
        }
        return requestTime(address, NTP_PORT, timeout);
    }

    public boolean requestTime(InetAddress address, int port, int timeout) {
        DatagramSocket socket = null;
        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);
            // 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 (DBG) {
                Log.d(TAG, "round trip: " + roundTripTime + "ms, " +
                        "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;//计算得到的ntp时间
            mNtpTimeReference = responseTicks;//读取响应的时间节点
            mRoundTripTime = roundTripTime;
        } catch (Exception e) {
            if (DBG) Log.d(TAG, "request time failed: " + e);
            return false;
        } finally {
            if (socket != null) {
                socket.close();
            }
        }

        return true;
    }
一个标准的socket发送请求-读取响应的模式。实现中,记录了一些特殊的时间节点,我们需要注意。

系统是如何判定当前NTP时间是否需要刷新呢?

            // force refresh NTP cache when outdated
            if (mTime.getCacheAge() >= mPollingIntervalMs) {//如果这次时间更新的时间节点和上一次时间更新时获取到结果的时间节点之间的差异大于mPollingIntervalMs时,认为时间过期,需要强制刷新时间;
                mTime.forceRefresh();
            }
    @Override
    public long getCacheAge() {
        if (mHasCache) {
            return SystemClock.elapsedRealtime() - mCachedNtpElapsedRealtime;//mCachedNtpElapsedRealtime = client.getNtpTimeReference();
        } else {
            return Long.MAX_VALUE;
        }
    }
    /**
     * Returns the reference clock value (value of SystemClock.elapsedRealtime())
     * corresponding to the NTP time.
     *
     * @return reference clock corresponding to the NTP time.
     */
    public long getNtpTimeReference() {
        return mNtpTimeReference;
    }

具体的信息可以参考里面的注释。NTP时间刷新完成后,我们还需要将它设置到系统中,其实也就是设置到kernel中:

            // only update when NTP time is fresh
            if (mTime.getCacheAge() < mPollingIntervalMs) {//这种情况下认为NTP时间是新鲜的、有效的
                final long ntp = mTime.currentTimeMillis();//获取当前的NTP时间
                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);//将NTP时间设置到内核中,此时时间才会生效
                    }
                } else {
                    if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
                }
                mLastNtpFetchTime = SystemClock.elapsedRealtime();
            } 

判断获取到的NTP时间的有效性,随后SystemClock.setCurrentTimeMillis()函数会将时间设置到内核中,让其生效;这样我们的时间就得到更新了。

如果某次的时间刷新操作失败了,系统会设置定时任务,在某个时间段之后再去触发时间刷新:

     * Cancel old alarm and starts a new one for the specified interval.
     *
     * @param interval when to trigger the alarm, starting from now.
     */
    private void resetAlarm(long interval) {
        mAlarmManager.cancel(mPendingPollIntent);
        long now = SystemClock.elapsedRealtime();
        long next = now + interval;
        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
    }
定时器任务触发时,会引起如下处理:
	//用于定时器
    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));
    }
向MyHandler发送EVENT_POLL_NETWORK_TIME事件,再次去调用onPollNetworkTime()函数处理当前的时间刷新请求,重复之前的分析流程。


NetworkTimeUpdateService服务启动及NTP时间刷新流程就分析完成了。





Android NTP机制详解(基于Android 9.0) Android NTP机制详细分析(基于Android 9.0) 阅读详情

相关推荐

RK3568安卓11网络时间同步终极指南:从基础配置到内核级修改

本文详细解析了在RK3568安卓11平台上实现高精度网络时间同步(NTP)的完整方案。从基础的Overlay配置修改与ADB动态调试,到深入定制NetworkTimeUpdateService内核服务逻辑,提供了从快速上手到工业级高精度定制的全链路实践指南,帮助开发者解决时间同步失效、精度不足等核心问题。

rose2的博客 677

网络时间同步机制

android 7.1网络时间同步机制流程解析

qq_21750755的博客 3812

深入解析Android NTP时间同步机制:从NetworkTimeUpdateService到SystemClock

本文深入解析了Android系统中基于NTP协议的时间同步机制。详细剖析了从NetworkTimeUpdateService服务启动、三大触发场景,到通过SntpClient向NTP服务器请求时间,最终由SystemClock修正系统时钟的完整流程,并探讨了其异常处理与重试机制,帮助开发者理解系统时间自动校准背后的原理。

f9g0h的博客 561

Android P系统时间更新分析

@[TOC] Android P系统时间更新分析 概述 框架 代码位置 代码分析 参考资料 框架 代码位置 frameworks/base/services/java/com/android/server/SystemServer.java frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateServic...

liu-yonggang的专栏 931

Android自动时间同步那些坑:为什么你的设备时间总是不准?

本文深入剖析了Android设备时间不准的根源,聚焦于其自动时间同步机制的核心组件NetworkTimeUpdateService、SntpClient与SystemClock。文章详细解读了从NTP服务器配置、网络权限到系统服务逻辑等环节的常见“坑位”,并为开发者提供了从诊断到应用层兜底策略的实战指南,旨在帮助解决因时间不同步导致的应用稳定性问题。

weixin_29200485的博客 1407

Android时间同步那些事儿:NetworkTimeUpdateService工作原理与自定义NTP服务器配置

本文深入解析了Android系统中NetworkTimeUpdateService的工作原理与同步策略,详细介绍了NTP协议在系统架构中的角色及SntpClient等核心组件的协作流程。重点探讨了如何通过修改系统属性、源码预置及应用层实现三种方法,为Android设备配置自定义NTP服务器,以满足企业级应用对高精度时间同步的严苛需求。

rgv2345678的博客 216

Android8.1 时间更新机制之网络更新时间和GPS更新时间

一、Android网络时间更新 Android网络时间更新,大体分两类。1、moderm相关更新,2、网络更新。本次主要介绍网路更新时间,主要涉及到NetworkTimeUpdateService,该类运行在SystemServer(ActivityManagerService)进程中。它有点特殊,从名字来看,其实Service,其实它和WifiService、ConnectivityManagerService等系统Service不同。 ZygoteInit.java的startSystemServer函

wq892373445的博客 3499

Android实时显示时间日期(极简)

Android实时显示时间日期(极简)

是阿超 3652

[RK3288][Android6.0] WiFi之网络时间定期同步更新

Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92前面有分析了网络时间同步过程,每次开机的时候会同步时间,现在有考虑到一个问题,当一直开机的时候,由于RTC的硬件可能偏移从而导致时间错误的可能,那么NetworkTimeUpdateService这一块有没有定期更新呢,事实上是有的,利用的是alarm和广播机制NetworkTimeUpdateS

Kris Fei's blog 3154

Linux yum源配置以及yum命令讲解

前面为大家介绍了RPM二进制包安装软件,RPM包安装的最大的缺点也提到过,就是依赖性太强,一旦遇到依赖问题,则需要我们手动解决包之间具有依赖性的问题,今天为大家介绍一种可自动解决包之间依赖关系的安装方式:yum。 yum 全称“Yellow dog Updater, Modified”,是一个专门为了解决包的依赖关系而存在的软件包管理器。就好像 Windows 系统上我们通常可以通过各种软件管家实现软件的一键安装、升级和卸载,yum就是Linux 系统中的一键安装工具。yum 解决依赖关系的基本逻辑是:yu

九派Linux的博客 2283

告别LM2596!用LM5175打造400W自动升降压数控电源(附完整电路图与代码)

本文详细介绍了如何利用LM5175芯片设计400W自动升降压数控电源,替代传统的LM2596方案。通过四开关Buck-Boost拓扑结构,LM5175实现了高达98%的转换效率和400W大功率输出,支持无缝升降压切换。文章涵盖芯片选型、电路设计、PCB布局及STM32数控实现,为工程师提供完整的实践指南。

weixin_27032989的博客 598

Android系统时间同步NetworkTimeUpdateService

Android系统时间更新有两种方式:NITZ时间和NTP时间,NITZ由运营商提供,通过sim卡更新时间,NTP为通过网络,访问服务器获取时间,这里主要介绍NTP的方式。 NetworkTimeUpdateService启动 NetworkTimeUpdateService作为Android系统服务,在SystemServer中启动 private void startOtherServices...

qq_34211365的博客 2924

R语言绘图中文显示全攻略:从乱码到完美适配

本文详细解析了R语言绘图中的中文显示问题,从乱码根源到完美适配方案。通过showtext、sysfonts和extrafont等工具包的应用,提供跨平台的中文显示解决方案,涵盖ggplot2图表、PDF/PNG输出及Shiny应用等场景,助力用户高效实现R绘图中文完美呈现。

hp777的博客 258

Android 时间更新机制之网络更新时间

综述:Android网络时间更新,大体分两类。1、moderm相关更新,2、网络更新。本次主要介绍android设备网路时间同步。

hailushijie的专栏 8208

【人工智能】提示词工程:让大模型精准响应的核心技术,提示词的基础框架 以及 常见提示词框架及场景

编写提示词的基础原则包括:简单、明确、详细,提示词拥有零样本提示、少样本提示、分配角色、提示写作风格、限定输出格式等技巧。

奶爸的编程之路,也就一周冷个三天 1138

ICC2:使用analyze_lib_cell_placement检查lib cell的pass rate

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧?在place之前需要使analyze_lib_cell_placement命令去检查lib cell在当前的site row/power plan/legalize设置的环境下能够正常place和legalize。下面分享一个脚本去报告pass rate低于2%的lib cell。

m0_61544122的博客 674

android源码分析之网络更新时间

android网络更新时间 android时间的更新方式,一般有moderm和网络更新两种,MTK在此基础上,添加了GPS的方式。现在分析的是网络更新时间的方式 从网络更新只能更新时间但是无法改变时区;从手机网络运营商处获取时间可以获得时间和分区(这种方式大陆的运营商支持率很低) android系统中更新时间的操作 分为系统部分和应用部分 代码路径 /frameworks/base/service

Android 系统开发 4007

2025年GESP3月认证C++五级真题解析

小 A 有 2n 件物品,小 B 和小 C 想从小 A 手上买走这些物品。对于第 i 件物品,小 B 会以 bi 的价格购买,而小 C 会以 ci 的价格购买。对于所有测试点,保证 1 <= T <= 100, 2 <= g < p <= 10^9, g, p 为整数, p 为质数。对于所有测试点,保证 1 <= n <= 10^5, 0 <= bi, ci <= 10^9, bi, ci 为整数。对于另外 50 % 的测试点,保证 n <= 100000, 0 <= bi, ci <= 10^9。

lan_in的博客 1035

android8.x 连接网络无法自动更新时间

android通过访问NTP服务器来更新系统时间,代码位于: frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java ***//连接网络和打开设置中的自动更新时间都会调用如下方法,来获取时间*** private void onPollNetworkTime(int event)...

weixin_40872563的博客 1561

信贷模型域——客户管理阶段模型(贷中模型)

本文主要探讨了信贷模型域中的客户管理阶段模型,重点聚焦于交叉销售模型。交叉销售模型不仅应用于贷中阶段,还贯穿于贷前、贷中、贷后全过程,其核心作用在于提升客户价值、增强客户粘性、降低流失率以及实现风险与收益的平衡。文中通过具体案例展示了交叉销售模型在现实业务中的应用,并指出其在贷中阶段的主要任务是提升存量客户价值、增加产品使用率,同时有效控制风险。

庄小焱 1879

汽车IVI中控开发入门及进阶(九):显示屏

显示屏Display panel和触控屏Touch panel,可以说随着汽车四化的进展,屏越来越多,越来越大,越来越高清,成为IVI中控、智能座舱系统的重要组成部分。比如如下一个电阻触摸屏。正文:显示屏主要功能就是显示,车载内容和信息的传递全靠显示屏,目前车载显示屏的种类可以分为:LCD液晶显示屏(TFT(薄膜晶体管)驱动的有源矩阵液晶显示,再细分IPS、TFT、SLCD,这几个子类)、LED显示屏(用 LED发光二极管作为背光光源)、OLED 显示屏。

码农17年的博客 2526

浅谈Android8.1自动同步时间机制

好久没更新博客了,这一年来实在是太忙,上班、洗衣、做饭、看书、奶爸,忙确实是忙。最近工作中刚好研究了下Android8.1系统的自动同步时间机制,也学到了一点东西,leader也叫分享下,趁着这个机会就简单分享下我对Android8.1自动同步时间机制的理解: 一、问题前沿 公司的测试妹子提了个问题:链接上wifi...

小飞哥的专栏 7161

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Android 时间同步功能 NTP NITZ

      Android中通过NTP服务器获取时间功能源码分析 https://blog.youkuaiyun.com/i2cbus/article/details/21550901 1 相关文件: frameworks\base\services\java\com\android\server\ SystemServer.java frameworks\base\services\java\co...

dc3120的专栏 2592

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

android ntp时间服务器同步时间,android NTP时间同步

android NTP时间同步相关文件:frameworks/base/services/java/com/android/server/SystemServer.javaframeworks/base/services/java/com/android/server/NetworkTimeUpdateService.javaframeworks/base/core/java/android/ut...

weixin_39607837的博客 1115

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Android系统的NTP时间更新

Android系统用于网络时间更新的最重要的类NetworkTimeUpdateService,在SystemServer中初始化调用,NetworkTimeUpdateService类中的依次进行构造函数和systemRunning方法进行初始化工作,获取时间的机制,在规定连接次数里面采用每隔一分钟获取NTP时间一次,超过连接次数采用240h周期更新一次NTP时间; 构造函数 pub

yezi121363的专栏 4787
上一篇: Android -- Audio系统之AudioTrack内部实现简析(三)
下一篇: Xml解析 - XmlPullParser解析示例分析
第一序列丶
博客等级 码龄11年 249粉丝 77原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值