文章目录
- 原始代码梳理
- 修改Android代码实现多NTP服务器轮询
- [Android Framework文章](#Android Framework文章)
在 Android AOSP(Android Open Source Project)中,默认使用 NetworkTimeUpdateService 来通过 NTP(Network Time Protocol)同步系统时间。其核心实现依赖于 SntpClient、NtpTrustedTime、NetworkTimeUpdateService这三个类。
Android8.1在NetworkTimeUpdateService.java中自带有多Ntp服务器的支持,可以直接在它基础上改就行,有两种改法。
Android11以及上却是没有这个,与Android8.1会有区别,只列一种改法。
原始代码梳理
先理下原生NTP时间同步调用的关键代码:
frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
其会在onPollNetworkTimeUnderWakeLock函数中调用NtpTrustedTime.forceRefresh()进行Ntp时间同步
java
private final NtpTrustedTime mTime;
mTime = NtpTrustedTime.getInstance(context);
private void onPollNetworkTimeUnderWakeLock(int event) {
// Force an NTP fix when outdated
NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
mTime.forceRefresh();
cachedNtpResult = mTime.getCachedTimeResult();
}
}
接着NtpTrustedTime的forceRefresh中调用的是SntpClient.requestTime函数:
frameworks/base/core/java/android/util/NtpTrustedTime.java
注意,有些平台会对原生代码进行定制,比如展锐的某SDK代码,是用SprdSntpClient替换了SntpClient:

修改Android代码实现多NTP服务器轮询
修改 frameworks/base/core/res/res/values/config.xml
列几个国内的ntp域名
<!-- 主 NTP 域名(默认 time.android.com,可替换为自定义域名) -->
<string name="config_ntpServer" translatable="false">time.android.com</string>
修改 NtpTrustedTime.java
NtpTrustedTime.java会调用SntpClient进行时间同步,
frameworks/base/core/java/android/util/NtpTrustedTime.java
原生代码

核心就是这几行:
java
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;
}
代码修改
-
添加头文件

-
添加成员变量

-
构造函数中初始化ntp服务器列表

-
重新实现forceRefresh函数
@UnsupportedAppUsage
public boolean forceRefresh() {
synchronized (this) {
if (mServerList.isEmpty()) {
// 服务器列表为空,重新初始化
initializeServerList();
}ConnectivityManager connectivityManager = mConnectivityManagerSupplier.get(); if (connectivityManager == null) { if (LOGD) Log.d(TAG, "forceRefresh: no ConnectivityManager"); return false; } final Network network = connectivityManager.getActiveNetwork(); final NetworkInfo ni = connectivityManager.getNetworkInfo(network); if (ni == null || !ni.isConnected()) { if (LOGD) Log.d(TAG, "forceRefresh: no connectivity"); return false; } final int timeoutMillis = getTimeoutMillis(); if (LOGD) Log.d(TAG, "forceRefresh() from cache miss, servers: " + mServerList); String successfulServer = null; boolean success = false; // 按当前优化顺序尝试所有服务器 for (String server : mServerList) { if (LOGD) Log.d(TAG, "Attempting NTP sync with server: " + server); final SntpClient client = new SntpClient(); if (client.requestTime(server, timeoutMillis, network)) { long ntpCertainty = client.getRoundTripTime() / 2; mTimeResult = new TimeResult( client.getNtpTime(), client.getNtpTimeReference(), ntpCertainty); successfulServer = server; success = true; if (LOGD) Log.d(TAG, "Successfully synced with server: " + server); // 优化服务器顺序 optimizeServerOrder(successfulServer); break; // 成功立即跳出循环 } else { if (LOGD) Log.w(TAG, "Failed to sync with server: " + server); } } if (!success) { if (LOGD) Log.e(TAG, "All " + mServerList.size() + " servers failed"); // 所有服务器都失败时重置顺序 resetServerOrderOnCompleteFailure(); } return success; } } -
新增函数
initializeServerList
optimizeServerOrder
resetServerOrderOnCompleteFailure
getRecentSuccessServers
getTimeoutMillis
setServerList
以下是整个实现的patch:
diff
diff --git a/frameworks/base/core/java/android/util/NtpTrustedTime.java b/frameworks/base/core/java/android/util/NtpTrustedTime.java
index 990d34c..f089ab1 100755
--- a/frameworks/base/core/java/android/util/NtpTrustedTime.java
+++ b/frameworks/base/core/java/android/util/NtpTrustedTime.java
@@ -34,6 +34,10 @@ import android.text.TextUtils;
import com.android.internal.annotations.GuardedBy;
import java.util.Objects;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
import java.util.function.Supplier;
/**
@@ -95,13 +99,25 @@ public class NtpTrustedTime implements TrustedTime {
}
private static final String TAG = "NtpTrustedTime";
- private static final boolean LOGD = false;
+ private static final boolean LOGD = true;
private static NtpTrustedTime sSingleton;
@NonNull
private final Context mContext;
+ // 智能服务器列表管理
+ private final ArrayList<String> mServerList = new ArrayList<>();
+ private final HashMap<String, Long> mServerSuccessTime = new HashMap<>();
+ private static final long SERVER_PREFERENCE_DURATION = 24 * 60 * 60 * 1000; // 24小时
+ private static final String[] DEFAULT_SERVER_LIST = {
+ "time.google.com",
+ "pool.ntp.org",
+ "ntp.aliyun.com",
+ "time.windows.com",
+ "time.apple.com"
+ };
+
/**
* A supplier that returns the ConnectivityManager. The Supplier can return null if
* ConnectivityService isn't running yet.
@@ -128,6 +144,8 @@ public class NtpTrustedTime implements TrustedTime {
private NtpTrustedTime(Context context) {
mContext = Objects.requireNonNull(context);
+ // 初始化服务器列表
+ initializeServerList();
}
@UnsupportedAppUsage
@@ -139,14 +157,62 @@ public class NtpTrustedTime implements TrustedTime {
return sSingleton;
}
+ /**
+ * 初始化服务器列表
+ */
+ private void initializeServerList() {
+ synchronized (this) {
+ mServerList.clear();
+
+ // 获取配置的默认服务器
+ NtpConnectionInfo connectionInfo = getNtpConnectionInfo();
+ String defaultServer = null;
+ if (connectionInfo != null) {
+ defaultServer = connectionInfo.getServer();
+ }
+
+ // 1. 添加默认服务器
+ if (defaultServer != null && !defaultServer.isEmpty()) {
+ mServerList.add(defaultServer);
+ }
+
+ // 2. 添加其他默认服务器(排除重复)
+ for (String server : DEFAULT_SERVER_LIST) {
+ if (!mServerList.contains(server)) {
+ mServerList.add(server);
+ }
+ }
+
+ // 3. 根据历史成功记录优化初始顺序
+ optimizeInitialServerOrder();
+
+ if (LOGD) Log.d(TAG, "Initialized server list: " + mServerList);
+ }
+ }
+
+ /**
+ * 优化初始服务器顺序
+ */
+ private void optimizeInitialServerOrder() {
+ List<String> recentSuccessServers = getRecentSuccessServers();
+ if (!recentSuccessServers.isEmpty()) {
+ // 将最近成功过的服务器移到最前面
+ for (String server : recentSuccessServers) {
+ if (mServerList.contains(server) && !server.equals(mServerList.get(0))) {
+ mServerList.remove(server);
+ mServerList.add(0, server);
+ }
+ }
+ if (LOGD) Log.d(TAG, "Optimized initial server order: " + mServerList);
+ }
+ }
+
@UnsupportedAppUsage
public boolean forceRefresh() {
synchronized (this) {
- NtpConnectionInfo connectionInfo = getNtpConnectionInfo();
- if (connectionInfo == null) {
- // missing server config, so no trusted time available
- if (LOGD) Log.d(TAG, "forceRefresh: invalid server config");
- return false;
+ if (mServerList.isEmpty()) {
+ // 服务器列表为空,重新初始化
+ initializeServerList();
}
ConnectivityManager connectivityManager = mConnectivityManagerSupplier.get();
@@ -161,20 +227,157 @@ public class NtpTrustedTime implements TrustedTime {
return false;
}
- if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
- //final SntpClient client = new SntpClient();
- final SntpClient client = new SntpClient();
- final String serverName = connectionInfo.getServer();
- final int timeoutMillis = connectionInfo.getTimeoutMillis();
- if (client.requestTime(serverName, timeoutMillis, network)) {
- long ntpCertainty = client.getRoundTripTime() / 2;
- mTimeResult = new TimeResult(
- client.getNtpTime(), client.getNtpTimeReference(), ntpCertainty);
- return true;
- } else {
- return false;
+ final int timeoutMillis = getTimeoutMillis();
+ if (LOGD) Log.d(TAG, "forceRefresh() from cache miss, servers: " + mServerList);
+
+ String successfulServer = null;
+ boolean success = false;
+
+ // 按当前优化顺序尝试所有服务器
+ for (String server : mServerList) {
+ if (LOGD) Log.d(TAG, "Attempting NTP sync with server: " + server);
+
+ final SntpClient client = new SntpClient();
+ if (client.requestTime(server, timeoutMillis, network)) {
+ long ntpCertainty = client.getRoundTripTime() / 2;
+ mTimeResult = new TimeResult(
+ client.getNtpTime(), client.getNtpTimeReference(), ntpCertainty);
+ successfulServer = server;
+ success = true;
+ if (LOGD) Log.d(TAG, "Successfully synced with server: " + server);
+
+ // 优化服务器顺序
+ optimizeServerOrder(successfulServer);
+ break; // 成功立即跳出循环
+ } else {
+ if (LOGD) Log.w(TAG, "Failed to sync with server: " + server);
+ }
+ }
+
+ if (!success) {
+ if (LOGD) Log.e(TAG, "All " + mServerList.size() + " servers failed");
+ // 所有服务器都失败时重置顺序
+ resetServerOrderOnCompleteFailure();
+ }
+
+ return success;
+ }
+ }
+
+ /**
+ * 优化服务器顺序 - 将成功的服务器移到最前面
+ */
+ private void optimizeServerOrder(String successfulServer) {
+ if (successfulServer == null || !mServerList.contains(successfulServer)) {
+ return;
+ }
+
+ // 记录成功时间
+ mServerSuccessTime.put(successfulServer, System.currentTimeMillis());
+
+ // 如果已经在最前面,不需要调整
+ if (successfulServer.equals(mServerList.get(0))) {
+ if (LOGD) Log.d(TAG, "Successful server " + successfulServer + " is already first");
+ return;
+ }
+
+ // 将成功的服务器移到最前面
+ mServerList.remove(successfulServer);
+ mServerList.add(0, successfulServer);
+
+ if (LOGD) Log.d(TAG, "Optimized server order: " + mServerList);
+ }
+
+ /**
+ * 当所有服务器都失败时重置服务器顺序
+ */
+ private void resetServerOrderOnCompleteFailure() {
+ // 保留历史成功记录,但重置服务器顺序为默认顺序
+ ArrayList<String> newOrder = new ArrayList<>();
+
+ // 优先添加最近成功过的服务器
+ List<String> recentSuccessServers = getRecentSuccessServers();
+ newOrder.addAll(recentSuccessServers);
+
+ // 添加其他服务器(按默认顺序)
+ for (String server : DEFAULT_SERVER_LIST) {
+ if (!newOrder.contains(server)) {
+ newOrder.add(server);
+ }
+ }
+
+ // 确保配置的默认服务器在列表中
+ NtpConnectionInfo connectionInfo = getNtpConnectionInfo();
+ if (connectionInfo != null) {
+ String defaultServer = connectionInfo.getServer();
+ if (defaultServer != null && !newOrder.contains(defaultServer)) {
+ newOrder.add(0, defaultServer);
+ }
+ }
+
+ mServerList.clear();
+ mServerList.addAll(newOrder);
+
+ if (LOGD) Log.d(TAG, "Reset server order after complete failure: " + mServerList);
+ }
+
+ /**
+ * 获取最近成功过的服务器列表
+ */
+ private List<String> getRecentSuccessServers() {
+ List<String> recentServers = new ArrayList<>();
+ long currentTime = System.currentTimeMillis();
+
+ for (String server : mServerList) {
+ Long lastSuccess = mServerSuccessTime.get(server);
+ if (lastSuccess != null &&
+ (currentTime - lastSuccess) < SERVER_PREFERENCE_DURATION) {
+ recentServers.add(server);
}
}
+
+ // 按最近成功时间排序(最近的成功排在最前面)
+ recentServers.sort((s1, s2) -> {
+ Long time1 = mServerSuccessTime.get(s1);
+ Long time2 = mServerSuccessTime.get(s2);
+ return Long.compare(time2, time1); // 降序排列
+ });
+
+ return recentServers;
+ }
+
+ /**
+ * 获取超时时间
+ */
+ private int getTimeoutMillis() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ final Resources res = mContext.getResources();
+ final int defaultTimeoutMillis = res.getInteger(
+ com.android.internal.R.integer.config_ntpTimeout);
+ return Settings.Global.getInt(
+ resolver, Settings.Global.NTP_TIMEOUT, defaultTimeoutMillis);
+ }
+
+ /**
+ * 添加自定义服务器列表
+ */
+ public void setServerList(String[] servers) {
+ synchronized (this) {
+ if (servers == null || servers.length == 0) {
+ return;
+ }
+
+ mServerList.clear();
+
+ // 添加自定义服务器
+ for (String server : servers) {
+ if (!mServerList.contains(server)) {
+ mServerList.add(server);
+ }
+ }
+
+ if (LOGD) Log.d(TAG, "Updated server list: " + mServerList);
+ }
}
预期行为
text
第一次同步: [google, apple, nist]
→ google失败 → apple成功 → 列表变为: [apple, google, nist]
第二次同步: [apple, google, nist]
→ apple成功 → 列表保持: [apple, google, nist]
第三次同步: [apple, google, nist]
→ apple失败 → google成功 → 列表变为: [google, apple, nist]
以下是开机后,第一次联网时ntp同步的打印:

实测代码有效果,android13这块代码基本与android11一样,可用同样方法修改。
Android Framework文章
Android8 Framework实现Ntp服务器多域名轮询同步时间
Android11~13 Framework实现Ntp服务器多域名轮询同步时间
作者:帅得不敢出门 谢绝转载。
