Android 设置/修改系统NTP服务地址

发布于:2025-08-12 ⋅ 阅读:(17) ⋅ 点赞:(0)

在这里插入图片描述

Android 手机的 NTP 时间同步(网络时间同步)主要依赖网络,但系统时间来源还包括其他方式,整体时间校准机制是多种来源的结合。具体可分为以下几类:

1. 网络 NTP 同步(最主要方式)

这是 Android 设备获取时间的核心方式,通过访问 NTP 服务器实现:

  • 原理:设备连接 Wi-Fi 或移动数据(蜂窝网络)时,会定期向预设的 NTP 服务器(如 time.android.com、厂商自定义服务器等)发送请求,获取标准时间并校准本地时间。
  • 特点:依赖网络连接,精度通常在毫秒到秒级,是日常使用中最主要的时间来源。

2. GPS/位置服务(辅助时间来源)

GPS 不仅提供位置信息,也会同步时间:

  • 原理:GPS 卫星内置高精度原子钟,设备接收 GPS 信号时,会同时获取卫星的精确时间(UTC 时间),并结合时区信息转换为本地时间。
  • 特点
    • 精度极高(毫秒级),不受网络限制,户外定位时自动同步。
    • 通常作为网络同步的补充,尤其在网络不稳定或无网络时提供时间参考。
    • 部分设备在开启“位置服务”后,会优先使用 GPS 时间校准系统时间。

3. 移动网络(蜂窝网络)时间

部分运营商的移动网络(如 4G/5G)会通过信令传递时间信息:

  • 原理:设备接入蜂窝网络时,可能从基站获取时间(类似 NTP 的简化机制),尤其在早期 2G/3G 网络中更常见。
  • 特点:精度较低(通常秒级),依赖运营商网络配置,现代设备更多以 NTP 同步为主。

4. 本地保存的时间(离线临时使用)

设备断电或重启时,会依赖内置的 RTC(实时时钟)芯片维持基本时间:

  • 原理:RTC 芯片由设备内置电池供电,即使主电源关闭也能运行,保存最近同步的时间。
  • 特点:精度低(可能每天偏差几秒到几分钟),仅作为离线时的临时时间来源,联网后会立即通过 NTP 或 GPS 校准。

总的来说, Android 手机的时间来源是多方式协同的:

  • 主要来源:网络 NTP 同步(最常用,依赖网络)。
  • 辅助来源:GPS 时间(高精度,依赖定位信号)、蜂窝网络时间(运营商提供)。
  • fallback 机制:本地 RTC 时钟(离线时临时使用)。

系统会根据网络状态、定位信号强度等自动选择最优时间来源,确保时间准确性。例如:联网时优先用 NTP,户外定位时结合 GPS 校准,离线时依赖 RTC 并在联网后修正偏差。


NTP 服务:

frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java

    public NetworkTimeUpdateService(Context context) {
        mContext = context;
        mTime = NtpTrustedTime.getInstance(context);
        mAlarmManager = mContext.getSystemService(AlarmManager.class);
        mTimeDetector = mContext.getSystemService(TimeDetector.class);
        mCM = mContext.getSystemService(ConnectivityManager.class);

        Intent pollIntent = new Intent(ACTION_POLL, null);
        // Broadcast alarms sent by system are immutable
        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent,
                PendingIntent.FLAG_IMMUTABLE);

        mPollingIntervalMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpPollingInterval);
        mPollingIntervalShorterMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpPollingIntervalShorter);
        mTryAgainTimesMax = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_ntpRetry);

        mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
                PowerManager.PARTIAL_WAKE_LOCK, TAG);
    }
    private void onPollNetworkTimeUnderWakeLock(int event) {
        long currentElapsedRealtimeMillis = SystemClock.elapsedRealtime();
        // Force an NTP fix when outdated
        NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
        if (cachedNtpResult == null || cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis)
                >= mPollingIntervalMs) {
            if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
            boolean isSuccessful = mTime.forceRefresh();
            if (isSuccessful) {
                mTryAgainCounter = 0;
            } else {
                String logMsg = "forceRefresh() returned false: cachedNtpResult=" + cachedNtpResult
                        + ", currentElapsedRealtimeMillis=" + currentElapsedRealtimeMillis;

                if (DBG) {
                    Log.d(TAG, logMsg);
                }
                mLocalLog.log(logMsg);
            }

            cachedNtpResult = mTime.getCachedTimeResult();
        }
        //....
    }

frameworks/base/core/java/android/util/NtpTrustedTime.java

    @GuardedBy("this")
    private NtpConnectionInfo getNtpConnectionInfo() {
        final ContentResolver resolver = mContext.getContentResolver();

        final Resources res = mContext.getResources();

        final String hostname;
        if (mHostnameForTests != null) {
            hostname = mHostnameForTests;
        } else {
            String serverGlobalSetting =
                    Settings.Global.getString(resolver, Settings.Global.NTP_SERVER);
            if (serverGlobalSetting != null) {
                hostname = serverGlobalSetting;
            } else {
                hostname = res.getString(com.android.internal.R.string.config_ntpServer);
            }
        }

        final Integer port;
        if (mPortForTests != null) {
            port = mPortForTests;
        } else {
            port = SntpClient.STANDARD_NTP_PORT;
        }

        final int timeoutMillis;
        if (mTimeoutForTests != null) {
            timeoutMillis = (int) mTimeoutForTests.toMillis();
        } else {
            int defaultTimeoutMillis =
                    res.getInteger(com.android.internal.R.integer.config_ntpTimeout);
            timeoutMillis = Settings.Global.getInt(
                    resolver, Settings.Global.NTP_TIMEOUT, defaultTimeoutMillis);
        }
        return TextUtils.isEmpty(hostname) ? null :
            new NtpConnectionInfo(hostname, port, timeoutMillis);
    }

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public boolean forceRefresh() {
        synchronized (this) {
            NtpConnectionInfo connectionInfo = getNtpConnectionInfo();
            if (connectionInfo == null) {
                // missing server config, so no NTP time available
                if (LOGD) Log.d(TAG, "forceRefresh: invalid server config");
                return false;
            }

            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);

            // This connectivity check is to avoid performing a DNS lookup for the time server on a
            // unconnected network. There are races to obtain time in Android when connectivity
            // changes, which means that forceRefresh() can be called by various components before
            // the network is actually available. This led in the past to DNS lookup failures being
            // cached (~2 seconds) thereby preventing the device successfully making an NTP request
            // when connectivity had actually been established.
            // A side effect of check is that tests that run a fake NTP server on the device itself
            // will only be able to use it if the active network is connected, even though loopback
            // addresses are actually reachable.
            if (ni == null || !ni.isConnected()) {
                if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");
                return false;
            }

            if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
            final SntpClient client = new SntpClient();
            final String serverName = connectionInfo.getServer();
            final int port = connectionInfo.getPort();
            final int timeoutMillis = connectionInfo.getTimeoutMillis();
            if (client.requestTime(serverName, port, timeoutMillis, network)) {
                long ntpCertainty = client.getRoundTripTime() / 2;
                mTimeResult = new TimeResult(
                        client.getNtpTime(), client.getNtpTimeReference(), ntpCertainty);
                return true;
            } else {
                return false;
            }
        }
    }

系统默认采用android的NTP服务器:

frameworks/base/core/res/res/values/config.xml

<string translatable="false" name="config_ntpServer">2.android.pool.ntp.org</string>

frameworks/base/core/java/android/provider/Settings.java

       /** Preferred NTP server. {@hide} */
       public static final String NTP_SERVER = "ntp_server";

通常, 系统没有提供可视界面供用户设置NTP服务器的接口. 可以通过adb命令来设置:

# 设置NTP服务器
adb shell settings put global "ntp_server" "ntp.aliyun.com"

# 关闭 和 打开 自动时间同步
adb shell settings put global "auto_time" 0
adb shell settings put global "auto_time" 1

实际测试发现, AOSP中默认的NTP服务器地址, 经常是访问不上的, 所以, 可以考虑更换为aliyun的ntp服务器;
测试过程:

  1. 关闭自动设置时间(auto_time)
  2. 更改当前系统时间为任意非准确时间
  3. 关闭网络/有SIM卡可以拔出来
  4. 重启系统, 清除NTP缓存数据, 否则, 系统会从缓存的NTP数据来更新当前系统时间
  5. 启动后时间是错误的, 打开WIFI, 打开自动设置时间, 正常情况下系统时间成功更新

适用性

  1. 国内的手机厂商大部分采用的是自定义的NTP服务端
  2. 某些厂商的设备(比如Oculus Quest 系列)在国内水土不服, 建议修改为国内的服务器
  3. 某些特定的行业需要自主的NTP服务器.

参考

网络时间检测
在这里插入图片描述