Android-NTP时间同步机制

Android-NTP时间同步机制

最近收到一个需求,要求屏蔽原生时间同步设置系统时间的地方,换成广播把时间发送给中间件(这个也是捣鼓了很久)。在完成这个需求之后,后面测试反馈有个bug说是时间回溯了,看了下log发现是连接网络后,重启设备,从开机到回连设备花了大概一分多钟的才同步时间。先是叫负责网络同事帮忙看了下,发现网络是正常连接而且也是有网的。刚好通过这个bug再加深下ntp时间同步机制(因为是Android14,跟之前的版本改动也是比较大)

老规矩我们就按代码的调用顺序来说,直接就从监听到有网络开始吧(梦开始的地方),有网络之后就会调用onPollNetworkTime

\android14\frameworks\base\services\core\java\com\android\server\timedetector\NetworkTimeUpdateService.java

onPollNetworkTime

less 复制代码
 // All callbacks will be invoked using mHandler because of how the callback is registered.
    private class NetworkConnectivityCallback extends ConnectivityManager.NetworkCallback {
        @Override
        public void onAvailable(@NonNull Network network) {
            Log.d(TAG, String.format("New default network %s; checking time.", network));
            synchronized (mLock) {
                mDefaultNetwork = network;
            }
​
            // Running on mHandler so invoke directly.
            Log.i(TAG, "wukexiang--network available");
            onPollNetworkTime("network available");
        }
​
        @Override
        public void onLost(@NonNull Network network) {
            synchronized (mLock) {
                if (network.equals(mDefaultNetwork)) {
                    mDefaultNetwork = null;
                }
            }
        }
    }
​
​
private void onPollNetworkTime(@NonNull String reason) {
        Network network;
        synchronized (mLock) {
            network = mDefaultNetwork;
        }
​
        mWakeLock.acquire();
        try {
            Log.i(TAG, "wukexiang--network refreshAndRescheduleIfRequired");
            mEngine.refreshAndRescheduleIfRequired(network, reason, mRefreshCallbacks);
        } finally {
            mWakeLock.release();
        }
    }

refreshAndRescheduleIfRequired

然后就到了最关键的refreshAndRescheduleIfRequired,这个函数也是很复杂,我们一步一步来看

less 复制代码
@Override
        public void refreshAndRescheduleIfRequired(
                @Nullable Network network, @NonNull String reason,
                @NonNull RefreshCallbacks refreshCallbacks) {
             //Log.i("wukexiang", Log.getStackTraceString(new Throwable()));
            Log.i(TAG, "wukexiang--network refreshAndRescheduleIfRequired in ");
            //没有网络,对时就是无米之谈。如果 network 为空,直接退出,等待下一次网络可用信号触发
            if (network == null) {
                Log.i(TAG, "wukexiang--network refreshAndRescheduleIfRequired network == null ");
                // If we don't have any default network, don't do anything: When a new network
                // is available then this method will be called again.
                logToDebugAndDumpsys("refreshIfRequiredAndReschedule:"
                        + " reason=" + reason
                        + ": No default network available. No refresh attempted and no next"
                        + " attempt scheduled.");
                return;
            }
​
            // Step 1: Work out if the latest time result, if any, needs to be refreshed and handle
            // the refresh.
​
            // A refresh should be attempted if there is no latest time result, or if the latest
            // time result is considered too old.
            //查缓存:看看本地有没有之前存好的时间(initialTimeResult)。
            //2. 算年龄:当前时间减去上次对时时间,得到 timeResultAgeMillis。
            //3. 做决定:如果时间太老了(超过了 24 小时的 mNormalPollingIntervalMillis),并且允许刷新(没被频繁请求限制),那么 shouldAttemptRefresh 就为 true。
            NtpTrustedTime.TimeResult initialTimeResult = mNtpTrustedTime.getCachedTimeResult();
            boolean shouldAttemptRefresh;
            synchronized (this) {
                long currentElapsedRealtimeMillis = mElapsedRealtimeMillisSupplier.get();
​
                // calculateTimeResultAgeMillis() safely handles a null initialTimeResult.
                long timeResultAgeMillis = calculateTimeResultAgeMillis(
                        initialTimeResult, currentElapsedRealtimeMillis);
                shouldAttemptRefresh =
                        timeResultAgeMillis >= mNormalPollingIntervalMillis
                        && isRefreshAllowed(currentElapsedRealtimeMillis);
            }
​
            boolean refreshSuccessful = false;
            if (shouldAttemptRefresh) {
                Log.i(TAG, "wukexiang--network refreshAndRescheduleIfRequired shouldAttemptRefresh " + shouldAttemptRefresh);
                // This is a blocking call. Deliberately invoked without holding the "this" monitor
                // to avoid blocking other logic that wants to use the "this" monitor, e.g. dump().
                //这是一个阻塞操作,它会真正去请求 ntp 时间,这里先按下不表
                refreshSuccessful = tryRefresh(network);
            }
​
            synchronized (this) {
                // This section of code deliberately doesn't assume it is the only component using
                // the NtpTrustedTime singleton to obtain NTP times: another component in the same
                // process could be gathering NTP signals (which then won't have been suggested to
                // the time detector).
                // TODO(b/222295093): Make this class the sole user of the NtpTrustedTime singleton
                //  and simplify / reduce duplicate suggestions and other logic.
                NtpTrustedTime.TimeResult latestTimeResult = mNtpTrustedTime.getCachedTimeResult();
​
                // currentElapsedRealtimeMillis is used to evaluate ages and refresh scheduling
                // below. Capturing this after obtaining the cached time result ensures that latest
                // time result ages will be >= 0.
                long currentElapsedRealtimeMillis = mElapsedRealtimeMillisSupplier.get();
​
                long latestTimeResultAgeMillis = calculateTimeResultAgeMillis(
                        latestTimeResult, currentElapsedRealtimeMillis);
​
                // Step 2: Set mTryAgainCounter.
                //   + == 0: The last attempt was successful OR the latest time result is acceptable
                //           OR the mTryAgainCounter exceeded mTryAgainTimesMax and has been reset
                //           to 0. In all these cases the normal refresh interval should be used.
                //   + > 0: The last refresh attempt was unsuccessful. Some number of retries are
                //          allowed using the short interval depending on mTryAgainTimesMax.
                //mTryAgainCounter(重试计数器) 是关键。
                //• 如果对时失败(比如 DNS 解析不到),计数器变成 1。
               //• 只要计数器大于 0,后面就会选择"短间隔模式"
                if (shouldAttemptRefresh) {
                    if (refreshSuccessful) {
                        mTryAgainCounter = 0;
                    } else {
                        if (mTryAgainTimesMax < 0) {
                            // When mTryAgainTimesMax is negative there's no enforced maximum and
                            // short intervals should be used until a successful refresh. Setting
                            // mTryAgainCounter to 1 is sufficient for the interval calculations
                            // below, i.e. there's no need to increment.
                            mTryAgainCounter = 1;
                        } else {
                            mTryAgainCounter++;
                            if (mTryAgainCounter > mTryAgainTimesMax) {
                                mTryAgainCounter = 0;
                            }
                        }
                    }
                }
                if (latestTimeResultAgeMillis < mNormalPollingIntervalMillis) {
                    // The latest time result may indicate a successful refresh has been achieved by
                    // another user of the NtpTrustedTime singleton. This could be an "else if", but
                    // this is deliberately done defensively in all cases to maintain the invariant
                    // that mTryAgainCounter will be 0 if the latest time result is currently ok.
                    mTryAgainCounter = 0;
                }
​
                // Step 3: Suggest the latest time result to the time detector if it is fresh
                // regardless of whether a refresh happened / succeeded above. The time detector
                // service can detect duplicate suggestions and not do more work than it has to, so
                // there is no need to avoid making duplicate suggestions.
                //即使刚才 tryRefresh 失败了(比如 DNS 报错),但如果 mNtpTrustedTime 缓存里还有一个 1 小时前成功的旧时间,系统认为这个旧时间也比本地那个乱跳的硬件时钟准。• 于是它会调用 makeNetworkTimeSuggestion,把这个"次优解"推给 TimeDetectorService。
                if (latestTimeResultAgeMillis < mNormalPollingIntervalMillis) {
                    Log.i(TAG, "wukexiang--network refreshAndRescheduleIfRequired makeNetworkTimeSuggestion ");
                    makeNetworkTimeSuggestion(latestTimeResult, reason, refreshCallbacks);
                }
​
                // Step 4: (Re)schedule the next refresh attempt based on the latest state.
​
                // Determine which refresh attempt delay to use by using the current value of
                // mTryAgainCounter.
                // 如果前面判断计数器大于 0,这里就会选择"短间隔模式",然后用两个分支来决定下次更新时间的具体时间戳(nextRefreshElapsedRealtimeMillis)
                long refreshAttemptDelayMillis = mTryAgainCounter > 0
                        ? mShortPollingIntervalMillis : mNormalPollingIntervalMillis;
​
                // The refresh attempt delay is applied to a different point in time depending on
                // whether a refresh attempt is overdue to ensure the refresh attempt scheduling
                // acts correctly / safely, i.e. won't schedule actions for immediate execution or
                // in the past.
                long nextRefreshElapsedRealtimeMillis;
                //当前时间依然有效,比如你刚对时成功,或者别人刚更新了缓存。系统会基于那个成功的时间点加上间隔。
                if (latestTimeResultAgeMillis < refreshAttemptDelayMillis) {
                    // The latestTimeResultAgeMillis and refreshAttemptDelayMillis indicate a
                    // refresh attempt is not yet due.  This branch uses the elapsed realtime of the
                    // latest time result to calculate when the latest time result will become too
                    // old and the next refresh attempt will be due.
                    //
                    // Possibilities:
                    //   + A refresh was attempted and successful, mTryAgainCounter will be set
                    //     to 0, refreshAttemptDelayMillis == mNormalPollingIntervalMillis, and this
                    //     branch will execute.
                    //   + No refresh was attempted, but something else refreshed the latest time
                    //     result held by the NtpTrustedTime.
                    //
                    // If a refresh was attempted but was unsuccessful, latestTimeResultAgeMillis >=
                    // mNormalPollingIntervalMillis (because otherwise it wouldn't be attempted),
                    // this branch won't be executed, and the one below will be instead.
                    nextRefreshElapsedRealtimeMillis =
                            latestTimeResult.getElapsedRealtimeMillis() + refreshAttemptDelayMillis;
                     Log.i(TAG, "wukexiang--network latestTimeResultAgeMillis < " + latestTimeResult.getElapsedRealtimeMillis()+ "---" + refreshAttemptDelayMillis);
                } else if (mLastRefreshAttemptElapsedRealtimeMillis != null) {
                    //当前时间无效或已过期,当你第一次失败时,mTryAgainCounter 变为了 1,refreshAttemptDelayMillis 变为了 60 秒。
                    //• 系统会计算:上次尝试的时间 + 60秒。这个60s就是在config配置文件中的<integer name="config_ntpPollingIntervalShorter">60000</integer>,默认为60s
                    //• 拦截逻辑:即使你在第 10 秒又调用了这个函数(因为网络又可用了),这个函数依然会计算出相同的 nextRefreshElapsedRealtimeMillis。它发现"下一次排班"还没到,所以就不会再次触发 tryRefresh。
                    // This branch is executed when the latest time result is missing, or it's older
                    // than refreshAttemptDelayMillis. There may already have been attempts to
                    // refresh the network time that have failed, so the important point for this
                    // branch is not how old the latest time result is, but when the last refresh
                    // attempt took place:
                    //   + If a refresh was just attempted (and failed), then
                    //     mLastRefreshAttemptElapsedRealtimeMillis will be close to
                    //     currentElapsedRealtimeMillis.
                    //   + If a refresh was not just attempted, for a refresh not to have been
                    //     attempted EITHER:
                    //     + The latest time result must be < mNormalPollingIntervalMillis ago
                    //       (would be handled by the branch above)
                    //     + A refresh wasn't allowed because {time since last refresh attempt}
                    //       < mShortPollingIntervalMillis, so
                    //       (mLastRefreshAttemptElapsedRealtimeMillis + refreshAttemptDelayMillis)
                    //       would have to be in the future regardless of the
                    //       refreshAttemptDelayMillis value. This ignores the execution time
                    //       between the "current time" used to work out whether a refresh needed to
                    //       happen, and "current time" used to compute the last time result age,
                    //       but a single short interval shouldn't matter.
                    nextRefreshElapsedRealtimeMillis =
                            mLastRefreshAttemptElapsedRealtimeMillis + refreshAttemptDelayMillis;
                    Log.i(TAG, "wukexiang--network mLastRefreshAttemptElapsedRealtimeMillis != null " + mLastRefreshAttemptElapsedRealtimeMillis + "---" + refreshAttemptDelayMillis);
                } else {
                    // This branch should never execute: mLastRefreshAttemptElapsedRealtimeMillis
                    // should always be non-null because a refresh should always be attempted at
                    // least once above. Regardelss, the calculation below should result in safe
                    // scheduling behavior.
                    String logMsg = "mLastRefreshAttemptElapsedRealtimeMillis unexpectedly missing."
                            + " Scheduling using currentElapsedRealtimeMillis";
                    Log.w(TAG, logMsg);
                    logToDebugAndDumpsys(logMsg);
                    nextRefreshElapsedRealtimeMillis =
                            currentElapsedRealtimeMillis + refreshAttemptDelayMillis;
​
                    Log.i(TAG, "wukexiang--network mLastRefreshAttemptElapsedRealtimeMillis else " + currentElapsedRealtimeMillis + "---" + refreshAttemptDelayMillis);
                }
            // Defensive coding to guard against bad scheduling / logic errors above: Try to
                // ensure that alarms aren't scheduled in the past.
                if (nextRefreshElapsedRealtimeMillis <= currentElapsedRealtimeMillis) {
                    String logMsg = "nextRefreshElapsedRealtimeMillis is a time in the past."
                            + " Scheduling using currentElapsedRealtimeMillis instead";
                    Log.w(TAG, logMsg);
                    logToDebugAndDumpsys(logMsg);
                    nextRefreshElapsedRealtimeMillis =
                            currentElapsedRealtimeMillis + refreshAttemptDelayMillis;
                }
                 Log.i(TAG, "wukexiang--network refreshAndRescheduleIfRequired refreshCallbacks.scheduleNextRefresh " + reason + network + nextRefreshElapsedRealtimeMillis);
                //最后在这里是再次刷新的回调
                refreshCallbacks.scheduleNextRefresh(nextRefreshElapsedRealtimeMillis);
​
                logToDebugAndDumpsys("refreshIfRequiredAndReschedule:"
                        + " network=" + network
                        + ", reason=" + reason
                        + ", initialTimeResult=" + initialTimeResult
                        + ", shouldAttemptRefresh=" + shouldAttemptRefresh
                        + ", refreshSuccessful=" + refreshSuccessful
                        + ", currentElapsedRealtimeMillis="
                        + formatElapsedRealtimeMillis(currentElapsedRealtimeMillis)
                        + ", latestTimeResult=" + latestTimeResult
                        + ", mTryAgainCounter=" + mTryAgainCounter
                        + ", refreshAttemptDelayMillis=" + refreshAttemptDelayMillis
                        + ", nextRefreshElapsedRealtimeMillis="
                        + formatElapsedRealtimeMillis(nextRefreshElapsedRealtimeMillis));
            }
        }

makeNetworkTimeSuggestion

上面我们讲到,如果请求ntp时间成功,获取到最新的数据,就会调用makeNetworkTimeSuggestion这个函数来上报

less 复制代码
        /**
         * Suggests the network time to the time detector. It may choose use it to set the system
         * clock.
         */
        private void makeNetworkTimeSuggestion(@NonNull TimeResult timeResult,
                @NonNull String debugInfo, @NonNull RefreshCallbacks refreshCallbacks) {
            UnixEpochTime timeSignal = new UnixEpochTime(
                    timeResult.getElapsedRealtimeMillis(), timeResult.getTimeMillis());
            NetworkTimeSuggestion timeSuggestion =
                    new NetworkTimeSuggestion(timeSignal, timeResult.getUncertaintyMillis());
            timeSuggestion.addDebugInfo(debugInfo);
            timeSuggestion.addDebugInfo(timeResult.toString());
            refreshCallbacks.submitSuggestion(timeSuggestion);
        }

submitSuggestion

这里就也调用refreshCallbacks的回调来进行上报,跟上面的下次刷新回调是一个对象

less 复制代码
mRefreshCallbacks = new Engine.RefreshCallbacks() {
            @Override
            public void scheduleNextRefresh(@ElapsedRealtimeLong long elapsedRealtimeMillis) {
                alarmManager.cancel(pendingPollIntent);
                alarmManager.set(
                        AlarmManager.ELAPSED_REALTIME, elapsedRealtimeMillis, pendingPollIntent);
            }
​
            @Override
            public void submitSuggestion(NetworkTimeSuggestion suggestion) {
                Log.i(TAG, "wukexiang--submitSuggestion suggestNetworkTime");
                timeDetectorInternal.suggestNetworkTime(suggestion);
​
            }
        };

suggestNetworkTime

再到\android14\frameworks\base\services\core\java\com\android\server\timedetector\TimeDetectorInternalImpl.java

less 复制代码
 @Override
    public void suggestNetworkTime(@NonNull NetworkTimeSuggestion suggestion) {
        Objects.requireNonNull(suggestion);
​
        mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(suggestion));
    }

suggestNetworkTime

再到\android14\frameworks\base\services\core\java\com\android\server\timedetector\TimeDetectorStrategyImpl.java

less 复制代码
@Override
    public synchronized void suggestNetworkTime(@NonNull NetworkTimeSuggestion suggestion) {
        Log.i(LOG_TAG, "wukexiang--TimeDetectorStrategyImpl suggestNetworkTime");
        ConfigurationInternal currentUserConfig = mCurrentConfigurationInternal;
        if (DBG) {
            Slog.d(LOG_TAG, "Network suggestion received."
                    + " currentUserConfig=" + currentUserConfig
                    + " suggestion=" + suggestion);
        }
        Objects.requireNonNull(suggestion);
​
        if (!validateAutoSuggestionTime(suggestion.getUnixEpochTime(), suggestion)) {
            return;
        }
​
        // The caller submits suggestions with the best available information when there are network
        // changes. The best available information may have been cached and if they were all stored
        // this would lead to duplicates showing up in the suggestion history. The suggestions may
        // be made for different reasons but there is not a significant benefit to storing the same
        // suggestion information again. doAutoTimeDetection() should still be called: this ensures
        // the suggestion and device state are always re-evaluated, which might produce a different
        // detected time if, for example, the age of all suggestions are considered.
        NetworkTimeSuggestion lastNetworkSuggestion = mLastNetworkSuggestion.get();
        if (lastNetworkSuggestion == null || !lastNetworkSuggestion.equals(suggestion)) {
            mLastNetworkSuggestion.set(suggestion);
            notifyNetworkTimeUpdateListenersAsynchronously();
        }
​
        // Now perform auto time detection. The new suggestion may be used to modify the system
        // clock.
        String reason = "New network time suggested. suggestion=" + suggestion;
        Log.i(LOG_TAG, "wukexiang--TimeDetectorStrategyImpl doAutoTimeDetection" + reason);
        doAutoTimeDetection(reason);
    }

doAutoTimeDetection

最后就走到这个doAutoTimeDetection这里,他会根据不同的reason来执行对应的逻辑

java 复制代码
@GuardedBy("this")
    private void doAutoTimeDetection(@NonNull String detectionReason) {
        Log.i("wukexiang", Log.getStackTraceString(new Throwable()));
         Log.i(LOG_TAG, "wukexiang--doAutoTimeDetection");
        // Try the different origins one at a time.
        int[] originPriorities = mCurrentConfigurationInternal.getAutoOriginPriorities();
        for (int origin : originPriorities) {
            UnixEpochTime newUnixEpochTime = null;
            String cause = null;
            if (origin == ORIGIN_TELEPHONY) {
                Log.i(LOG_TAG, "wukexiang--doAutoTimeDetection origin == ORIGIN_TELEPHONY");
                TelephonyTimeSuggestion bestTelephonySuggestion = findBestTelephonySuggestion();
                if (bestTelephonySuggestion != null) {
                    newUnixEpochTime = bestTelephonySuggestion.getUnixEpochTime();
                    cause = "Found good telephony suggestion."
                            + ", bestTelephonySuggestion=" + bestTelephonySuggestion
                            + ", detectionReason=" + detectionReason;
                }
            } else if (origin == ORIGIN_NETWORK) {
                Log.i(LOG_TAG, "wukexiang--doAutoTimeDetection origin == ORIGIN_NETWORK");
                NetworkTimeSuggestion networkSuggestion = findLatestValidNetworkSuggestion();
                if (networkSuggestion != null) {
                    newUnixEpochTime = networkSuggestion.getUnixEpochTime();
                    cause = "Found good network suggestion."
                            + ", networkSuggestion=" + networkSuggestion
                            + ", detectionReason=" + detectionReason;
                }
            } else if (origin == ORIGIN_GNSS) {
                Log.i(LOG_TAG, "wukexiang--doAutoTimeDetection origin == ORIGIN_GNSS");
                GnssTimeSuggestion gnssSuggestion = findLatestValidGnssSuggestion();
                if (gnssSuggestion != null) {
                    newUnixEpochTime = gnssSuggestion.getUnixEpochTime();
                    cause = "Found good gnss suggestion."
                            + ", gnssSuggestion=" + gnssSuggestion
                            + ", detectionReason=" + detectionReason;
                }
            } else if (origin == ORIGIN_EXTERNAL) {
                Log.i(LOG_TAG, "wukexiang--doAutoTimeDetection origin == ORIGIN_EXTERNAL");
                ExternalTimeSuggestion externalSuggestion = findLatestValidExternalSuggestion();
                if (externalSuggestion != null) {
                    newUnixEpochTime = externalSuggestion.getUnixEpochTime();
                    cause = "Found good external suggestion."
                            + ", externalSuggestion=" + externalSuggestion
                            + ", detectionReason=" + detectionReason;
                }
            } else {
                 Log.i(LOG_TAG, "wukexiang--doAutoTimeDetection Unknown or unsupported origin=" + origin);
                Slog.w(LOG_TAG, "Unknown or unsupported origin=" + origin
                        + " in " + Arrays.toString(originPriorities)
                        + ": Skipping");
            }
            // Update the system clock if a good suggestion has been found.
            if (newUnixEpochTime != null) {
                if (mCurrentConfigurationInternal.getAutoDetectionEnabledBehavior()) {
                    Log.i(LOG_TAG, "wukexiang--doAutoTimeDetection setSystemClockAndConfidenceIfRequired");
                    setSystemClockAndConfidenceIfRequired(origin, newUnixEpochTime, cause);
                } else {
                    // An automatically detected time can be used to raise the confidence in the
                    // current time even if the device is set to only allow user input for the time
                    // itself.
                    Log.i(LOG_TAG, "wukexiang--doAutoTimeDetection upgradeSystemClockConfidenceIfRequired");
                    upgradeSystemClockConfidenceIfRequired(newUnixEpochTime, cause);
                }
                return;
            }
        }
​
        if (DBG) {
            Slog.d(LOG_TAG, "Could not determine time: No suggestion found in"
                    + " originPriorities=" + Arrays.toString(originPriorities)
                    + ", detectionReason=" + detectionReason);
        }
    }

setSystemClockAndConfidenceIfRequired

再到setSystemClockAndConfidenceIfRequired

less 复制代码
@GuardedBy("this")
    private boolean setSystemClockAndConfidenceIfRequired(
            @Origin int origin, @NonNull UnixEpochTime time, @NonNull String cause) {
​
        // Any time set through this class is inherently high confidence. Either it came directly
        // from a user, or it was detected automatically.
        @TimeConfidence final int newTimeConfidence = TIME_CONFIDENCE_HIGH;
        boolean isOriginAutomatic = isOriginAutomatic(origin);
        if (isOriginAutomatic) {
            if (!mCurrentConfigurationInternal.getAutoDetectionEnabledBehavior()) {
                if (DBG) {
                    Slog.d(LOG_TAG,
                            "Auto time detection is not enabled / no confidence update is needed."
                            + " origin=" + originToString(origin)
                            + ", time=" + time
                            + ", cause=" + cause);
                }
                return false;
            }
        } else {
            if (mCurrentConfigurationInternal.getAutoDetectionEnabledBehavior()) {
                if (DBG) {
                    Slog.d(LOG_TAG, "Auto time detection is enabled."
                            + " origin=" + originToString(origin)
                            + ", time=" + time
                            + ", cause=" + cause);
                }
                return false;
            }
        }
​
        mEnvironment.acquireWakeLock();
        try {
            return setSystemClockAndConfidenceUnderWakeLock(origin, time, newTimeConfidence, cause);
        } finally {
            mEnvironment.releaseWakeLock();
        }
    }

setSystemClockAndConfidenceUnderWakeLock

再到setSystemClockAndConfidenceUnderWakeLock,到这里就是真正的设置系统时间了setSystemClock,感兴趣的话也可以看看他每个步骤都做了什么操作,我们这里就不展开讲了

java 复制代码
@GuardedBy("this")
    private boolean setSystemClockAndConfidenceUnderWakeLock(
            @Origin int origin, @NonNull UnixEpochTime newTime,
            @TimeConfidence int newTimeConfidence, @NonNull String cause) {
        long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
        boolean isOriginAutomatic = isOriginAutomatic(origin);
        long actualSystemClockMillis = mEnvironment.systemClockMillis();
        if (isOriginAutomatic) {
            // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else
            // may be setting the clock.
            if (mLastAutoSystemClockTimeSet != null) {
                long expectedTimeMillis = mLastAutoSystemClockTimeSet.at(elapsedRealtimeMillis)
                        .getUnixEpochTimeMillis();
                long absSystemClockDifference =
                        Math.abs(expectedTimeMillis - actualSystemClockMillis);
                if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) {
                    Slog.w(LOG_TAG,
                            "System clock has not tracked elapsed real time clock. A clock may"
                                    + " be inaccurate or something unexpectedly set the system"
                                    + " clock."
                                    + " origin=" + originToString(origin)
                                    + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
                                    + " expectedTimeMillis=" + expectedTimeMillis
                                    + " actualTimeMillis=" + actualSystemClockMillis
                                    + " cause=" + cause);
                }
            }
        }
​
        // If the new signal would make sufficient difference to the system clock or mean a change
        // in confidence then system state must be updated.
​
        // Adjust for the time that has elapsed since the signal was received.
        long newSystemClockMillis = newTime.at(elapsedRealtimeMillis).getUnixEpochTimeMillis();
        long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
        long systemClockUpdateThreshold =
                mCurrentConfigurationInternal.getSystemClockUpdateThresholdMillis();
        boolean updateSystemClockRequired = absTimeDifference >= systemClockUpdateThreshold;
​
        @TimeConfidence int currentTimeConfidence = mEnvironment.systemClockConfidence();
        boolean updateConfidenceRequired = newTimeConfidence != currentTimeConfidence;
​
        if (updateSystemClockRequired) {
            String logMsg = "Set system clock & confidence."
                    + " origin=" + originToString(origin)
                    + " newTime=" + newTime
                    + " newTimeConfidence=" + newTimeConfidence
                    + " cause=" + cause
                    + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
                    + " (old) actualSystemClockMillis=" + actualSystemClockMillis
                    + " newSystemClockMillis=" + newSystemClockMillis
                    + " currentTimeConfidence=" + currentTimeConfidence;
            // if (origin == ORIGIN_NETWORK) {
            //      sendNtpTimeBroadcast(newSystemClockMillis,"updateSystemClockRequired");
            // }
            //在这里来真正的设置系统时间,最后的最后就是调用底层的那个set来设置系统时间
            mEnvironment.setSystemClock(newSystemClockMillis, newTimeConfidence, logMsg);
            if (DBG) {
                Slog.d(LOG_TAG, logMsg);
            }
​
            // CLOCK_PARANOIA : Record the last time this class set the system clock due to an
            // auto-time signal, or clear the record it is being done manually.
            if (isOriginAutomatic(origin)) {
                mLastAutoSystemClockTimeSet = newTime;
            } else {
                mLastAutoSystemClockTimeSet = null;
            }
        } else if (updateConfidenceRequired) {
            // Only the confidence needs updating. This path is separate from a system clock update
            // to deliberately avoid touching the system clock's value when it's not needed. Doing
            // so could introduce inaccuracies or cause unnecessary wear in RTC hardware or
            // associated storage.
            String logMsg = "Set system clock confidence."
                    + " origin=" + originToString(origin)
                    + " newTime=" + newTime
                    + " newTimeConfidence=" + newTimeConfidence
                    + " cause=" + cause
                    + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
                    + " (old) actualSystemClockMillis=" + actualSystemClockMillis
                    + " newSystemClockMillis=" + newSystemClockMillis
                    + " currentTimeConfidence=" + currentTimeConfidence;
            if (DBG) {
                Slog.d(LOG_TAG, logMsg);
            }
            mEnvironment.setSystemClockConfidence(newTimeConfidence, logMsg);
        } else {
            // Neither clock nor confidence need updating.
            //  if (origin == ORIGIN_NETWORK) {
            //      sendNtpTimeBroadcast(newSystemClockMillis,"clock no need updating");
            // }
            if (DBG) {
                Slog.d(LOG_TAG, "Not setting system clock or confidence."
                        + " origin=" + originToString(origin)
                        + " newTime=" + newTime
                        + " newTimeConfidence=" + newTimeConfidence
                        + " cause=" + cause
                        + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
                        + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
                        + " absTimeDifference=" + absTimeDifference
                        + " currentTimeConfidence=" + currentTimeConfidence);
            }
        }
        return true;
    }

Ntp时间同步流程就差不多了,然后我们再回到bug,重启回连居然要一分多钟才完成校准时间,我先是看了ntp相关打印,发现是每隔5秒就请求一次ntp,前几次是失败的,初步判断是ntp时间请求失败导致。那就看看是不是请求ntp时间链路的问题,找到这个SntpClient然后加上调用栈看看

yaml 复制代码
Line 128673: 03-14 11:33:11.739   940  1296 I ntp_failure: [ntp.ntsc.ac.cn/113.141.164.38,java.net.SocketTimeoutException: Poll timed out]
    Line 149674: 03-14 11:33:16.818   940  1296 I ntp_failure: [ntp.ntsc.ac.cn/1.82.219.234,java.net.SocketTimeoutException: Poll timed out]
    Line 169835: 03-14 11:33:21.831   940  1296 I ntp_failure: [ntp.ntsc.ac.cn/113.141.164.39,java.net.SocketTimeoutException: Poll timed out]
    Line 188564: 03-14 11:33:26.877   940  2143 I ntp_failure: [ntp.ntsc.ac.cn/113.141.164.38,java.net.SocketTimeoutException: Poll timed out]
    Line 188665: 03-14 11:33:27.002   940  2143 I ntp_success: [ntp.ntsc.ac.cn/1.82.219.234,121,278123347]
    03-14 11:33:27.002   940  2143 D SntpClient: round trip: 121ms, clock offset: 278123347ms

从log分析,在01:55:23.280网络就已经就绪了,但是在ntp请求的时候就抛出了异常,Unknown host,那就再看看SntpClient逻辑

yaml 复制代码
03-21 01:55:13.298  1140  1140 I time_detector: wukexiang--doAutoTimeDetection
03-21 01:55:13.298  1140  1140 I time_detector: wukexiang--doAutoTimeDetection origin == ORIGIN_TELEPHONY
03-21 01:55:13.298  1140  1140 I time_detector: wukexiang--doAutoTimeDetection origin == ORIGIN_NETWORK
03-21 01:55:13.298  1140  1140 I time_detector: wukexiang--doAutoTimeDetection origin == ORIGIN_GNSS
03-21 01:55:13.857  1140  1140 I NetworkTimeUpdateService: wukexiang--mShortPollingIntervalMillis 60000
03-21 01:55:23.280  1140  1388 I NetworkTimeUpdateService: wukexiang--network available
03-21 01:55:23.284  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired
03-21 01:55:23.284  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired in 
03-21 01:55:23.284  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired shouldAttemptRefresh true
03-21 01:55:23.486  1140  1388 I wukexiang: java.lang.Throwable
03-21 01:55:23.486  1140  1388 I wukexiang:     at android.net.SntpClient.requestTime(SntpClient.java:126)
03-21 01:55:23.486  1140  1388 I wukexiang:     at android.util.NtpTrustedTime$NtpTrustedTimeImpl.queryNtpServer(NtpTrustedTime.java:705)
03-21 01:55:23.486  1140  1388 I wukexiang:     at android.util.NtpTrustedTime.forceRefreshLocked(NtpTrustedTime.java:341)
03-21 01:55:23.486  1140  1388 I wukexiang:     at android.util.NtpTrustedTime.forceRefresh(NtpTrustedTime.java:273)
03-21 01:55:23.486  1140  1388 I wukexiang:     at com.android.server.timedetector.NetworkTimeUpdateService$EngineImpl.tryRefresh(NetworkTimeUpdateService.java:674)
03-21 01:55:23.486  1140  1388 I wukexiang:     at com.android.server.timedetector.NetworkTimeUpdateService$EngineImpl.refreshAndRescheduleIfRequired(NetworkTimeUpdateService.java:479)
03-21 01:55:23.486  1140  1388 I wukexiang:     at com.android.server.timedetector.NetworkTimeUpdateService.onPollNetworkTime(NetworkTimeUpdateService.java:215)
03-21 01:55:23.486  1140  1388 I wukexiang:     at com.android.server.timedetector.NetworkTimeUpdateService.-$$Nest$monPollNetworkTime(NetworkTimeUpdateService.java:0)
03-21 01:55:23.486  1140  1388 I wukexiang:     at com.android.server.timedetector.NetworkTimeUpdateService$NetworkConnectivityCallback.onAvailable(NetworkTimeUpdateService.java:249)
03-21 01:55:23.486  1140  1388 I wukexiang:     at android.net.ConnectivityManager$NetworkCallback.onAvailable(ConnectivityManager.java:3954)
03-21 01:55:23.486  1140  1388 I wukexiang:     at android.net.ConnectivityManager$NetworkCallback.onAvailable(ConnectivityManager.java:3936)
03-21 01:55:23.486  1140  1388 I wukexiang:     at android.net.ConnectivityManager$CallbackHandler.handleMessage(ConnectivityManager.java:4263)
03-21 01:55:23.486  1140  1388 I wukexiang:     at android.os.Handler.dispatchMessage(Handler.java:106)
03-21 01:55:23.486  1140  1388 I wukexiang:     at android.os.Looper.loopOnce(Looper.java:205)
03-21 01:55:23.486  1140  1388 I wukexiang:     at android.os.Looper.loop(Looper.java:294)
03-21 01:55:23.486  1140  1388 I wukexiang:     at android.os.HandlerThread.run(HandlerThread.java:67)
​
​
​
​
​
​
03-21 01:55:43.590  1140  1388 W wukexiang-SntpClient: wukexiang-Unknown host: ntp.aliyun.com5000
03-21 01:55:43.591  1140  1388 D wukexiang-SntpClient: wukexiang-request time failed
03-21 01:55:43.593  1140  1388 I NetworkTimeUpdateService: wukexiang--network mLastRefreshAttemptElapsedRealtimeMillis != null 35356---60000
03-21 01:55:43.593  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired refreshCallbacks.scheduleNextRefresh network available10395356
03-21 01:55:43.597  1140  1388 I NetworkTimeUpdateService: wukexiang--network available
03-21 01:55:43.600  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired
03-21 01:55:43.600  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired in 
03-21 01:55:43.600  1140  1388 I NetworkTimeUpdateService: wukexiang--network mLastRefreshAttemptElapsedRealtimeMillis != null 35356---60000
03-21 01:55:43.601  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired refreshCallbacks.scheduleNextRefresh network available10695356
03-21 01:55:43.623  1140  1388 I NetworkTimeUpdateService: wukexiang--network available
03-21 01:55:43.625  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired
03-21 01:55:43.625  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired in 
03-21 01:55:43.626  1140  1388 I NetworkTimeUpdateService: wukexiang--network mLastRefreshAttemptElapsedRealtimeMillis != null 35356---60000
03-21 01:55:43.627  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired refreshCallbacks.scheduleNextRefresh network available10395356
​
​
​
​
03-21 01:55:50.725  1140  1388 I NetworkTimeUpdateService: wukexiang--network available
03-21 01:55:50.726  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired
03-21 01:55:50.726  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired in 
03-21 01:55:50.727  1140  1388 I NetworkTimeUpdateService: wukexiang--network mLastRefreshAttemptElapsedRealtimeMillis != null 35356---60000
03-21 01:55:50.727  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired refreshCallbacks.scheduleNextRefresh network available10795356
03-21 01:55:51.541  1140  5570 I wukexiang: java.lang.Throwable
03-21 01:55:51.541  1140  5570 I wukexiang:     at android.net.SntpClient.requestTime(SntpClient.java:126)
03-21 01:55:51.541  1140  5570 I wukexiang:     at android.util.NtpTrustedTime$NtpTrustedTimeImpl.queryNtpServer(NtpTrustedTime.java:705)
03-21 01:55:51.541  1140  5570 I wukexiang:     at android.util.NtpTrustedTime.forceRefreshLocked(NtpTrustedTime.java:341)
03-21 01:55:51.541  1140  5570 I wukexiang:     at android.util.NtpTrustedTime.forceRefresh(NtpTrustedTime.java:264)
03-21 01:55:51.541  1140  5570 I wukexiang:     at com.android.server.location.gnss.NtpNetworkTimeHelper.blockingGetNtpTimeAndInject(NtpNetworkTimeHelper.java:185)
03-21 01:55:51.541  1140  5570 I wukexiang:     at com.android.server.location.gnss.NtpNetworkTimeHelper.$r8$lambda$Prd1O8ovcWG64n9K2U2sS7gDDm0(NtpNetworkTimeHelper.java:0)
03-21 01:55:51.541  1140  5570 I wukexiang:     at com.android.server.location.gnss.NtpNetworkTimeHelper$$ExternalSyntheticLambda0.run(R8$$SyntheticClass:0)
03-21 01:55:51.541  1140  5570 I wukexiang:     at java.lang.Thread.run(Thread.java:1012)
03-21 01:55:51.732  1140  5570 D wukexiang-SntpClient: round trip: 70ms, clock offset: 927343513ms
​
​
​
​
​
​
03-21 01:56:31.714  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired
03-21 01:56:31.714  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired in 
03-21 01:56:31.714  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired makeNetworkTimeSuggestion 
03-21 01:56:31.714  1140  1388 I NetworkTimeUpdateService: wukexiang--submitSuggestion suggestNetworkTime
03-21 01:56:31.715  1140  1388 I NetworkTimeUpdateService: wukexiang--network latestTimeResultAgeMillis < 63752---64800000
03-21 01:56:31.715  1140  1182 I time_detector: wukexiang--TimeDetectorStrategyImpl suggestNetworkTime
03-21 01:56:31.715  1140  1388 I NetworkTimeUpdateService: wukexiang--network refreshAndRescheduleIfRequired refreshCallbacks.scheduleNextRefresh scheduled refresh10764863752
03-21 01:56:31.715  1140  1182 I time_detector: wukexiang--TimeDetectorStrategyImpl doAutoTimeDetectionNew network time suggested. suggestion=NetworkTimeSuggestion{mUnixEpochTime=UnixEpochTime{mElapsedRealtimeMillis=63752, mUnixEpochTimeMillis=1774956695192}, mUncertaintyMillis=35, mDebugInfo=[scheduled refresh, TimeResult{unixEpochTime=2026-03-31T11:31:35.192Z, elapsedRealtime=PT1M3.752S, mUncertaintyMillis=35, mNtpServerSocketAddress=ntp.aliyun.com/203.107.6.88:123}]}
03-21 01:56:31.715  1140  1182 I wukexiang: java.lang.Throwable
03-21 01:56:31.715  1140  1182 I wukexiang:     at com.android.server.timedetector.TimeDetectorStrategyImpl.doAutoTimeDetection(TimeDetectorStrategyImpl.java:712)
03-21 01:56:31.715  1140  1182 I wukexiang:     at com.android.server.timedetector.TimeDetectorStrategyImpl.suggestNetworkTime(TimeDetectorStrategyImpl.java:330)
03-21 01:56:31.715  1140  1182 I wukexiang:     at com.android.server.timedetector.TimeDetectorInternalImpl.lambda$suggestNetworkTime$0(TimeDetectorInternalImpl.java:87)
03-21 01:56:31.715  1140  1182 I wukexiang:     at com.android.server.timedetector.TimeDetectorInternalImpl.$r8$lambda$i_bY-cYaOWr1U99VfGrEI33C7n4(TimeDetectorInternalImpl.java:0)
03-21 01:56:31.715  1140  1182 I wukexiang:     at com.android.server.timedetector.TimeDetectorInternalImpl$$ExternalSyntheticLambda1.run(R8$$SyntheticClass:0)
03-21 01:56:31.715  1140  1182 I wukexiang:     at android.os.Handler.handleCallback(Handler.java:958)
03-21 01:56:31.715  1140  1182 I wukexiang:     at android.os.Handler.dispatchMessage(Handler.java:99)
03-21 01:56:31.715  1140  1182 I wukexiang:     at android.os.Looper.loopOnce(Looper.java:205)
03-21 01:56:31.715  1140  1182 I wukexiang:     at android.os.Looper.loop(Looper.java:294)
03-21 01:56:31.715  1140  1182 I wukexiang:     at android.os.HandlerThread.run(HandlerThread.java:67)
03-21 01:56:31.715  1140  1182 I wukexiang:     at com.android.server.ServiceThread.run(ServiceThread.java:46)
03-21 01:56:31.715  1140  1182 I time_detector: wukexiang--doAutoTimeDetection
03-21 01:56:31.716  1140  1182 I time_detector: wukexiang--doAutoTimeDetection origin == ORIGIN_TELEPHONY
03-21 01:56:31.716  1140  1182 I time_detector: wukexiang--doAutoTimeDetection origin == ORIGIN_NETWORK
03-21 01:56:31.716  1140  1182 I time_detector: wukexiang--doAutoTimeDetection setSystemClockAndConfidenceIfRequired

\android14\frameworks\base\core\java\android\net\SntpClient.java

那我们来看看这个请求时间是怎么请求的,他是在需要解析DNS可能没有就绪导致解析不了地址,抛出异常。我也通过更换服务器,把原生的ntp.ntsc.ac.cn国家官方服务器改成阿里云的ntp.aliyun.com,结果也是一样会失败,排除服务器原因。我们再把这个timeout打出来确实也是5000ms,也就是5s来请求和log日志上一致,再经过几次的请求之后,后面也是成功获取到ntp时间。排除这里的原因,根本原因应该在后面流程

java 复制代码
/**
     * Sends an SNTP request to the given host and processes the response.
     *
     * @param host host name of the server.
     * @param port port of the server.
     * @param timeout network timeout in milliseconds. the timeout doesn't include the DNS lookup
     *                time, and it applies to each individual query to the resolved addresses of
     *                the NTP server.
     * @param network network over which to send the request.
     * @return true if the transaction was successful.
     */
    public boolean requestTime(String host, int port, int timeout, Network network) {
        Log.i("wukexiang", Log.getStackTraceString(new Throwable()));
        final Network networkForResolv = network.getPrivateDnsBypassingCopy();
        try {
            final InetAddress[] addresses = networkForResolv.getAllByName(host);
            for (int i = 0; i < addresses.length; i++) {
                if (requestTime(addresses[i], port, timeout, networkForResolv)) {
                    return true;
                }
            }
        } catch (UnknownHostException e) {
            Log.w(TAG, "wukexiang-Unknown host: " + host + timeout);
            EventLogTags.writeNtpFailure(host, e.toString());
        }
​
        if (DBG) Log.d(TAG, "wukexiang-request time failed");
        return false;
    }

上面日志里显示01:55:51.732时候我们ntp时间已经获取成功,应该直接往上报才对,但是并没有继续走刷新时间流程。我们在日志中打印中看到,应该是卡在刷新流程那了,把之前的refreshAndRescheduleIfRequired流程精简化看下,就知道原因了。关键点在于如果refreshSuccessful失败了,他就会走mTryAgainCounter重试计数器的流程, mTryAgainCounter置为1,refreshAttemptDelayMillis这个就会从24小时和mShortPollingIntervalMillis中选择短的这个,这个mShortPollingIntervalMillis我已经在日志中打印出来,正是60000ms也就是一分钟,到这里情况就很明朗了,再继续往下看。后面的逻辑就是判断如果你过来几秒网络又可以了,执行到这的时候,依旧会拦截,下次刷新的时间依旧是你之前的时间延时60s之后,再次刷新。

ini 复制代码
            //查缓存:看看本地有没有之前存好的时间(initialTimeResult)。
            //2. 算年龄:当前时间减去上次对时时间,得到 timeResultAgeMillis。
            //3. 做决定:如果时间太老了(超过了 24 小时的 mNormalPollingIntervalMillis),并且允许刷新(没被频繁请求限制),那么 shouldAttemptRefresh 就为 true。
            NtpTrustedTime.TimeResult initialTimeResult = mNtpTrustedTime.getCachedTimeResult();
            boolean shouldAttemptRefresh;
            synchronized (this) {
                long currentElapsedRealtimeMillis = mElapsedRealtimeMillisSupplier.get();
​
                // calculateTimeResultAgeMillis() safely handles a null initialTimeResult.
                long timeResultAgeMillis = calculateTimeResultAgeMillis(
                        initialTimeResult, currentElapsedRealtimeMillis);
                shouldAttemptRefresh =
                        timeResultAgeMillis >= mNormalPollingIntervalMillis
                        && isRefreshAllowed(currentElapsedRealtimeMillis);
            }
​
            boolean refreshSuccessful = false;
            if (shouldAttemptRefresh) {
                Log.i(TAG, "wukexiang--network refreshAndRescheduleIfRequired shouldAttemptRefresh " + shouldAttemptRefres
                //这是一个阻塞操作,它会真正去请求 ntp 时间,这里先按下不表
                refreshSuccessful = tryRefresh(network);
            }
​
            synchronized (this) {
             
                NtpTrustedTime.TimeResult latestTimeResult = mNtpTrustedTime.getCachedTimeResult()
                long currentElapsedRealtimeMillis = mElapsedRealtimeMillisSupplier.get();
                long latestTimeResultAgeMillis = calculateTimeResultAgeMillis(
                        latestTimeResult, currentElapsedRealtimeMillis);
​
                
                //mTryAgainCounter(重试计数器) 是关键。
                //• 如果对时失败(比如 DNS 解析不到),计数器变成 1。
               //• 只要计数器大于 0,后面就会选择"短间隔模式"
                if (shouldAttemptRefresh) {
                    if (refreshSuccessful) {
                        mTryAgainCounter = 0;
                    } else {
                        if (mTryAgainTimesMax < 0) {
                           
                            mTryAgainCounter = 1;
                        } else {
                            mTryAgainCounter++;
                            if (mTryAgainCounter > mTryAgainTimesMax) {
                                mTryAgainCounter = 0;
                            }
                        }
                    }
                }
                if (latestTimeResultAgeMillis < mNormalPollingIntervalMillis) {
                 
                    mTryAgainCounter = 0;
                }
​
                //即使刚才 tryRefresh 失败了(比如 DNS 报错),但如果 mNtpTrustedTime 缓存里还有一个 1 小时前成功的旧时间,系统认为这个旧时间也比本地那个乱跳的硬件时钟准。• 于是它会调用 makeNetworkTimeSuggestion,把这个"次优解"推给 TimeDetectorService。
                if (latestTimeResultAgeMillis < mNormalPollingIntervalMillis) {
                    Log.i(TAG, "wukexiang--network refreshAndRescheduleIfRequired makeNetworkTimeSuggestion ");
                    makeNetworkTimeSuggestion(latestTimeResult, reason, refreshCallbacks);
                }
​
                  
                // 如果前面判断计数器大于 0,这里就会选择"短间隔模式",然后用两个分支来决定下次更新时间的具体时间戳(nextRefreshElapsedRealtimeMillis)
                long refreshAttemptDelayMillis = mTryAgainCounter > 0
                        ? mShortPollingIntervalMillis : mNormalPollingIntervalMillis;
                long nextRefreshElapsedRealtimeMillis;
                //当前时间依然有效,比如你刚对时成功,或者别人刚更新了缓存。系统会基于那个成功的时间点加上间隔。
                if (latestTimeResultAgeMillis < refreshAttemptDelayMillis) {
                    nextRefreshElapsedRealtimeMillis =
                            latestTimeResult.getElapsedRealtimeMillis() + refreshAttemptDelayMillis;
                     Log.i(TAG, "wukexiang--network latestTimeResultAgeMillis < " + latestTimeResult.getElapsedRealtimeMillis()+ "---" + refreshAttemptDelayMillis);
                } else if (mLastRefreshAttemptElapsedRealtimeMillis != null) {
                    //当前时间无效或已过期,当你第一次失败时,mTryAgainCounter 变为了 1,refreshAttemptDelayMillis 变为了 60 秒。
                    //• 系统会计算:上次尝试的时间 + 60秒。这个60s就是在config配置文件中的<integer name="config_ntpPollingIntervalShorter">60000</integer>,默认为60s
                    //• 拦截逻辑:即使你在第 10 秒又调用了这个函数(因为网络又可用了),这个函数依然会计算出相同的 nextRefreshElapsedRealtimeMillis。它发现"下一次排班"还没到,所以就不会再次触发 tryRefresh。
                    nextRefreshElapsedRealtimeMillis =
                            mLastRefreshAttemptElapsedRealtimeMillis + refreshAttemptDelayMillis;
                    Log.i(TAG, "wukexiang--network mLastRefreshAttemptElapsedRealtimeMillis != null " + mLastRefreshAttemptElapsedRealtimeMillis + "---" + refreshAttemptDelayMillis);
                } else {
                    String logMsg = "mLastRefreshAttemptElapsedRealtimeMillis unexpectedly missing."
                            + " Scheduling using currentElapsedRealtimeMillis";
                    Log.w(TAG, logMsg);
                    logToDebugAndDumpsys(logMsg);
                    nextRefreshElapsedRealtimeMillis =
                            currentElapsedRealtimeMillis + refreshAttemptDelayMillis;
​
                    Log.i(TAG, "wukexiang--network mLastRefreshAttemptElapsedRealtimeMillis else " + currentElapsedRealtimeMillis + "---" + refreshAttemptDelayMillis);
                }
           
                if (nextRefreshElapsedRealtimeMillis <= currentElapsedRealtimeMillis) {
                    String logMsg = "nextRefreshElapsedRealtimeMillis is a time in the past."
                            + " Scheduling using currentElapsedRealtimeMillis instead";
                    Log.w(TAG, logMsg);
                    logToDebugAndDumpsys(logMsg);
                    nextRefreshElapsedRealtimeMillis =
                            currentElapsedRealtimeMillis + refreshAttemptDelayMillis;
                }
                 Log.i(TAG, "wukexiang--network refreshAndRescheduleIfRequired refreshCallbacks.scheduleNextRefresh " + reason + network + nextRefreshElapsedRealtimeMillis);
                //最后在这里是再次刷新的回调
                refreshCallbacks.scheduleNextRefresh(nextRefreshElapsedRealtimeMillis);
            }
        }

根据日志我们也可以看出。那么问题来了,这个60000ms是在哪设置的呢,我们全局搜索了下,可以得出是在配置文件里配置的,可以看到那个ntpTimeout也是在这设置的,那我们把60s改成10s就可以了,在网上搜索看到也有人改成8s

bash 复制代码
core/res/res/values/config.xml:    <string-array translatable="false" name="config_ntpServers">
core/res/res/values/config.xml:    <integer name="config_ntpTimeout">5000</integer>
core/res/res/values/config.xml:    <integer name="config_ntpPollingInterval">64800000</integer>
core/res/res/values/config.xml:    <integer name="config_ntpPollingIntervalShorter">60000</integer>
core/res/res/values/config.xml:    <integer name="config_ntpRetry">3</integer>
core/res/res/values/symbols.xml:  <java-symbol type="integer" name="config_ntpPollingInterval" />
core/res/res/values/symbols.xml:  <java-symbol type="integer" name="config_ntpPollingIntervalShorter" />
core/res/res/values/symbols.xml:  <java-symbol type="integer" name="config_ntpRetry" />
core/res/res/values/symbols.xml:  <java-symbol type="integer" name="config_ntpTimeout" />
core/res/res/values/symbols.xml:  <java-symbol type="array" name="config_ntpServers" />
core/java/android/util/NtpTrustedTime.java:                        res.getStringArray(com.android.internal.R.array.config_ntpServers);
core/java/android/util/NtpTrustedTime.java:                    res.getInteger(com.android.internal.R.integer.config_ntpTimeout);
相关推荐
Memory_荒年2 小时前
Dubbo面试通关秘籍:从“小白”到“源码大神”的终极指南
java·后端·dubbo
PFinal社区_南丞2 小时前
Go 1.26 go fix 详解:18 个分析器一键现代化代码
后端
beiju2 小时前
Agent Loop:AI Agent 的最小实现结构
后端·agent
野生技术架构师2 小时前
Spring Boot 4 与 Spring Framework 7 全面解析:新特性、升级要点与实战指南
spring boot·后端·spring
Java水解2 小时前
阿里国际Java社招面经分享(附赠阿里Java面试题)
java·后端·面试
Nyarlathotep01132 小时前
CyclicBarrier基础和原理
java·后端
菜鸟程序员专写BUG3 小时前
SpringBoot跨域报错全集|CORS、OPTIONS预检、无Access-Control报错全解决
spring boot·后端·状态模式
无籽西瓜a3 小时前
【西瓜带你学设计模式 | 第五期 - 建造者模式】建造者模式 —— 产品构建实现、优缺点与适用场景及模式区别
java·后端·设计模式·软件工程·建造者模式
小江的记录本4 小时前
【Spring注解】Spring生态常见注解——面试高频考点总结
java·spring boot·后端·spring·面试·架构·mvc