Android11~13 Framework实现Ntp服务器多域名轮询同步时间

文章目录

在 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域名

腾讯:time1.cloud.tencent.com

阿里:ntp.aliyun.com

复制代码
<!-- 主 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;
        }

代码修改

  1. 添加头文件

  2. 添加成员变量

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

  4. 重新实现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;
         }
     }
  5. 新增函数
    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文章

Android Framework专栏

Android8 Framework实现Ntp服务器多域名轮询同步时间
Android11~13 Framework实现Ntp服务器多域名轮询同步时间

作者:帅得不敢出门 谢绝转载。

相关推荐
计算机学姐38 分钟前
基于Python的新能源汽车数据可视化及分析系统【2026最新】
vue.js·python·信息可视化·django·flask·汽车·推荐算法
刘晓倩43 分钟前
Python的re
java·python·mysql
njsgcs44 分钟前
pyautocad 基于线段包围盒聚类
python·数据挖掘·聚类
qq_233907031 小时前
GEO优化企业2025推荐,提升网站全球访问速度与用户体验
大数据·人工智能·python·ux
阿雄不会写代码1 小时前
PPTX数据格式的更换图片
linux·运维·服务器
落羽的落羽1 小时前
【Linux系统】初探 虚拟地址空间
linux·运维·服务器·c++·人工智能·学习·机器学习
_OP_CHEN1 小时前
【Linux系统编程】(十一)从硬件基石到软件中枢:冯诺依曼体系与操作系统深度解析
linux·运维·服务器·操作系统·进程·冯诺依曼体系结构·os
Q_Q5110082851 小时前
python+django/flask+vue的高考志愿咨询系统
spring boot·python·django·flask·node.js·php