Android自动更新时间逻辑

3,927 阅读5分钟

Android系统更新时间的方式有两种:分别是NITZ和NTP。

NITZ:网络标识和时区(英语:Network Identity and Time Zone,缩写NITZ)是一种通过无线网络向移动设备提供本地日期和时间、时区、夏时制偏移,以及网络提供商身份信息的机制,这通常用于移动电话自动更新系统时间。NITZ技术自GSM 阶段2 Release 96版本开始成为官方标准的可选部分之一。

NTP:网络时间协议,英文名称:Network Time Protocol(NTP)是用来使计算机时间同步化的一种协议,它可以使计算机对其服务器或时钟源(如石英钟,GPS等等)做同步化,它可以提供高精准度的时间校正(LAN上与标准间差小于1毫秒,WAN上几十毫秒),且可介由加密确认的方式来防止恶毒的协议攻击。NTP的目的是在无序的Internet环境中提供精确和健壮的时间服务。

NTP

Android系统使用NTP自动更新系统时间的触发机制有两种:

  • 监听数据库字段AUTO_TIME,当这个字段发生变化的时候,会立即触发一次时间同步
  • 网络连接发生变化,当网络接通,会触发一次时间检查和同步
  • 定时更新机制,当预定的时间到了,会触发一次时间检查和同步

Android系统的使用NTP更新系统时间是在NetworkTimeUpdateService服务里面实现的,首先看一下服务的初始化过程。

NetworkTimeUpdateService初始化

这是一个others服务,即是一个可选服务。这个服务运行在system_server进程里面,是在SystemServer里面启动的。

frameworks/base/services/java/com/android/server/SystemServer.java

private void startOtherServices() {
    final Context context = mSystemContext;
    NetworkTimeUpdateService networkTimeUpdater = null;
    try {
        // 展开1:实例化,创建服务,并添加为系统服务
        networkTimeUpdater = new NetworkTimeUpdateService(context);
        ServiceManager.addService("network_time_update_service", networkTimeUpdater);
    } catch (Throwable e) {
        reportWtf("starting NetworkTimeUpdate service", e);
    }
    final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
    try {
        // 展开2:启动服务
        if (networkTimeUpdaterF != null) 
            networkTimeUpdaterF.systemRunning();
    } catch (Throwable e) {
        reportWtf("Notifying NetworkTimeService running", e);
    }    
}

展开1:实例化,创建服务

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

public NetworkTimeUpdateService(Context context) {
    mContext = context;
    // 创建NTP实例,这个就是使用NTP协议取得时间的类
    mTime = NtpTrustedTime.getInstance(context);
    // 取得AlarmManager实例
    mAlarmManager = mContext.getSystemService(AlarmManager.class);
    // 取得网络连接管理类的实例
    mCM = mContext.getSystemService(ConnectivityManager.class);

    Intent pollIntent = new Intent(ACTION_POLL, null);
    mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);

    // 从系统配置文件config.ini取得NTP时间参数
    // 默认是86400000ms,即1天
    mPollingIntervalMs = mContext.getResources().getInteger(
            com.android.internal.R.integer.config_ntpPollingInterval);
    // 由于网络原因,时间同步失败后,retry的时间间隔,默认是60000ms,即60s
    mPollingIntervalShorterMs = mContext.getResources().getInteger(
            com.android.internal.R.integer.config_ntpPollingIntervalShorter);
    // retry的次数,默认是3次
    mTryAgainTimesMax = mContext.getResources().getInteger(
            com.android.internal.R.integer.config_ntpRetry);
    // 时间误差,默认是5000ms,当时间误差超过5s,会更新系统时间
    mTimeErrorThresholdMs = mContext.getResources().getInteger(
            com.android.internal.R.integer.config_ntpThreshold);

    mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
            PowerManager.PARTIAL_WAKE_LOCK, TAG);
}

展开2:启动服务network_time_update_service

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

public void systemRunning() {
	// 接收来自NITZ更新系统时间后的广播
    registerForTelephonyIntents();

    // 注册Alarm,用于定时更新系统时间
    registerForAlarms();

    HandlerThread thread = new HandlerThread(TAG);
    thread.start();
    mHandler = new MyHandler(thread.getLooper());

    // 注册网络回调,当网络发生变化的时候会回调这个callback
    mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
    mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);

    // 创建ContentObserver,监听AUTO_TIME数据库字段的变化
    mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
    mSettingsObserver.observe(mContext);
}

监听AUTO_TIME更新系统时间

接着上面,分析一下SettingsObserver的工作原理。这块需要用到ContentObserver的知识。SettingsObserver继承ContentObserver,把需要监听的数据库字段传下去,

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

/** Observer to watch for changes to the AUTO_TIME setting */
private static class SettingsObserver extends ContentObserver {

    private int mMsg;
    private Handler mHandler;

    SettingsObserver(Handler handler, int msg) {
        super(handler);
        mHandler = handler;
        mMsg = msg;
    }

    void observe(Context context) {
        ContentResolver resolver = context.getContentResolver();
        resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
                false, this);
    }

    @Override
    public void onChange(boolean selfChange) {
        mHandler.obtainMessage(mMsg).sendToTarget();
    }
}

ContentObserver的工作原理不在这里展开,后面会专门开一篇文章来写ContentObserver。当Settings.Global.AUTO_TIME的值发生变化的时候,onChange()方法会被回调,即发送消息EVENT_AUTO_TIME_CHANGED。接受消息,更新时间的地方最后展开。

监听网络连接变化更新系统时间

private class NetworkTimeUpdateCallback extends NetworkCallback {
    @Override
    public void onAvailable(Network network) {
        Log.d(TAG, String.format("New default network %s; checking time.", network));
        mDefaultNetwork = network;
        // Running on mHandler so invoke directly.
        onPollNetworkTime(EVENT_NETWORK_CHANGED);
    }

    @Override
    public void onLost(Network network) {
        if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
    }
}

当网络连接建立的时候,会回调onAvailable()方法,即调用方法onPollNetworkTime()更新系统时间。

定时更新系统时间机制

private void registerForAlarms() {
    mContext.registerReceiver(
        new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
            }
        }, new IntentFilter(ACTION_POLL));
}

接收广播ACTION_POLL,然后发送消息EVENT_POLL_NETWORK_TIME来更新系统时间。

更新系统时间

/** Handler to do the network accesses on */
private class MyHandler extends Handler {

    public MyHandler(Looper l) {
        super(l);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case EVENT_AUTO_TIME_CHANGED:  // 接前面,AUTO_TIME数据库字段变化了
            case EVENT_POLL_NETWORK_TIME:  // 接前面,轮询时间到
            case EVENT_NETWORK_CHANGED:    // 接前面,新网络连接建立
                onPollNetworkTime(msg.what);
                break;
        }
    }
}

private void onPollNetworkTime(int event) {
    // If Automatic time is not set, don't bother. Similarly, if we don't
    // have any default network, don't bother.
    if (mDefaultNetwork == null) return;
    mWakeLock.acquire();
    try {
        onPollNetworkTimeUnderWakeLock(event);
    } finally {
        mWakeLock.release();
    }
}

private void onPollNetworkTimeUnderWakeLock(int event) {
    // Force an NTP fix when outdated
    // 距离上次更新时间大于等于1天,会重新从ntp server获取时间
    if (mTime.getCacheAge() >= mPollingIntervalMs) {
        if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
        mTime.forceRefresh();
    }
    // 如果成功从ntp server获取到时间,则重置定时器,并更新系统时间
    if (mTime.getCacheAge() < mPollingIntervalMs) {
        // Obtained fresh fix; schedule next normal update
        resetAlarm(mPollingIntervalMs);
        if (isAutomaticTimeRequested()) {
            updateSystemClock(event);
        }
    // 否则,进入retry环节
    } else {
        // No fresh fix; schedule retry
        mTryAgainCounter++;
        // retry次数没用完,则用60s重置定时器
        if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
            resetAlarm(mPollingIntervalShorterMs);
        // 否则,用1天重置定时器
        } else {
            // Try much later
            mTryAgainCounter = 0;
            resetAlarm(mPollingIntervalMs);
        }
    }
}

private void updateSystemClock(int event) {
	// 如果是用户打开自动更新时间的设置,则是force update
    final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
    if (!forceUpdate) {
    	// 如果一天之内通过NITZ更新过系统时间,则不再更新
        if (getNitzAge() < mPollingIntervalMs) {
            if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
            return;
        }

        final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
        // 当ntp server的时间和系统时间误差小于5s时,不再更新系统时间
        if (skew < mTimeErrorThresholdMs) {
            if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
            return;
        }
    }
    // 更新系统时间,实际是通过AlarmManagerService更新系统时间,这里不再展开
    SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
}

NtpTrustedTime

想要进一步查看Android系统ntp的实现代码,可以查看下面的源文件。

frameworks\base\core\java\android\util\TrustedTime.java
frameworks\base\core\java\android\util\NtpTrustedTime.java
frameworks\base\core\java\android\net\SntpClient.java

NITZ

NITZ相关的代码在telephony模块,在如下位置:

frameworks/opt/telephony

延伸学习

本文主要分享Android系统通过NTP自动更新系统时间的原理和实现过程。这里面用到了一些技术,大家可以延伸学习:

  • ContentObserver
  • PendingIntent
  • Alarm Manager/Service