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

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

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

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

xml 复制代码
<string translatable="false" name="config_ntpServer">2.android.pool.ntp.org</string>

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

java 复制代码
       /** Preferred NTP server. {@hide} */
       public static final String NTP_SERVER = "ntp_server";

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

bash 复制代码
# 设置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服务器.

参考

网络时间检测

相关推荐
沅霖1 小时前
下载Android studio
android·ide·android studio
xzkyd outpaper1 小时前
Kotlin 协程线程切换机制详解
android·开发语言·kotlin
Near_Li2 小时前
uniapp-使用mumu模拟器调试安卓APP
android·uni-app
zhangphil3 小时前
Android MediaMetadataRetriever取视频封面,Kotlin(1)
android·kotlin
onthewaying6 小时前
详解 Android GLSurfaceView 与 Renderer:开启你的 OpenGL ES 之旅
android·opengl
aqi006 小时前
FFmpeg开发笔记(八十)使用百变魔音AiSound实现变声特效
android·ffmpeg·音视频·直播·流媒体
xzkyd outpaper7 小时前
Android中RecyclerView基本使用
android
我命由我123458 小时前
Android 开发问题:The specified child already has a parent.
android·java·开发语言·java-ee·android jetpack·android-studio·android runtime
pengyu8 小时前
【Kotlin系统化精讲:肆】 | 数据类型之基本数据类型:代码世界里的砖瓦水泥沙
android·kotlin
三雒9 小时前
凡猿修仙传: Android SO 压缩方案 Nano
android