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服务器多域名轮询同步时间

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

相关推荐
Mico186 小时前
MySQL 8.0.35 基于GTID 主从复制安装增强半同步复制
android·mysql·adb
时光不染。回忆不淡6 小时前
Python基础语法教程
开发语言·python
一叶龙洲6 小时前
ubuntu26.04 xfce美化成mac
服务器·ubuntu·macos
Kapaseker6 小时前
你是不是还没用过 select?实战 Kotlin select
android·kotlin
Увидимся вчера6 小时前
绿盟堡垒机自动化任务—自动改密失败
linux·服务器·自动化
白执落6 小时前
使用 Python + LangChain + Vue3 构建 LLM 聊天应用
人工智能·python·langchain
蓝创工坊Blue Foundry6 小时前
本地 PDF、图片字段提取到 Excel:用文档工作台的完整流程
python·ocr·vim·paddlepaddle
小白说大模型6 小时前
AI Agent 调试实战:链路追踪、Prompt 可视化与异常定位的系统方法
java·人工智能·python·算法·prompt
●VON6 小时前
鸿蒙 PC Markdown 编辑器命令面板:把桌面高频操作收束到一个入口
服务器·华为·编辑器·harmonyos·鸿蒙
__Witheart__7 小时前
适用于Android内核boot.img生成流程
android·rockchip