There are two ways to update the time in Android: NITZ and NTP.
NITZ: Network Identity and Time Zone (NITZ) is a mechanism for providing mobile devices with local date and Time, Time Zone, daylight saving Time offset, and Network provider Identity information over a wireless Network. This is typically used by mobile phones to automatically update system Time. NITZ technology has been an optional part of the official standard since GSM Phase 2 Release 96.
NTP: Network time protocol. English name: Network Time Protocol (NTP) is a Protocol used to synchronize computer Time. It enables a computer to synchronize its server or clock source (such as quartz clock, GPS, etc.). It provides high precision Time correction (the difference between the standard and the LAN is less than 1 millisecond, WAN tens of milliseconds). And it can be used to prevent malicious protocol attacks by means of encryption confirmation. NTP is designed to provide accurate and robust time services in an out-of-order Internet environment.
NTP
The Android system uses NTP to automatically update the system time using two trigger mechanisms:
- Listen for the database field AUTO_TIME. When this field changes, a time synchronization is triggered immediately
- The network connection changes. When the network is connected, a time check and synchronization is triggered
- A timed update mechanism that triggers a time check and synchronization when a predetermined time is up
The Android system uses NTP to update the system time in NetworkTimeUpdateService. First, take a look at the initialization process of the service.
NetworkTimeUpdateService initialization
This is a others service, that is, an optional service. This service runs in the system_server process and is started in the SystemServer process.
frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices(a) {
final Context context = mSystemContext;
NetworkTimeUpdateService networkTimeUpdater = null;
try {
// Expand 1: instantiate, create a service, and add it as a system service
networkTimeUpdater = new NetworkTimeUpdateService(context);
ServiceManager.addService("network_time_update_service", networkTimeUpdater);
} catch (Throwable e) {
reportWtf("starting NetworkTimeUpdate service", e);
}
final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
try {
// Expand 2: start the service
if(networkTimeUpdaterF ! =null)
networkTimeUpdaterF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying NetworkTimeService running", e); }}Copy the code
Expansion 1: Instantiate and create the service
frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
public NetworkTimeUpdateService(Context context) {
mContext = context;
// Create an NTP instance. This is the class that uses the NTP protocol to obtain time
mTime = NtpTrustedTime.getInstance(context);
// Get the AlarmManager instance
mAlarmManager = mContext.getSystemService(AlarmManager.class);
// Get an instance of the network connection management class
mCM = mContext.getSystemService(ConnectivityManager.class);
Intent pollIntent = new Intent(ACTION_POLL, null);
mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
// Obtain NTP time parameters from the system configuration file config.ini
// The default value is 86400000ms, that is, 1 day
mPollingIntervalMs = mContext.getResources().getInteger(
com.android.internal.R.integer.config_ntpPollingInterval);
// Due to network reasons, the retry interval after time synchronization fails. The default value is 60000ms, that is, 60s
mPollingIntervalShorterMs = mContext.getResources().getInteger(
com.android.internal.R.integer.config_ntpPollingIntervalShorter);
// Retry times. The default is 3
mTryAgainTimesMax = mContext.getResources().getInteger(
com.android.internal.R.integer.config_ntpRetry);
// The time error is 5000ms by default. When the time error exceeds 5s, the system time is updated
mTimeErrorThresholdMs = mContext.getResources().getInteger(
com.android.internal.R.integer.config_ntpThreshold);
mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, TAG);
}
Copy the code
Expansion 2: Start the service network_time_update_service
frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
public void systemRunning(a) {
// Receive the broadcast from NITZ after the system time update
registerForTelephonyIntents();
// Register an Alarm to periodically update the system time
registerForAlarms();
HandlerThread thread = new HandlerThread(TAG);
thread.start();
mHandler = new MyHandler(thread.getLooper());
// Register the network callback, which will be called when the network changes
mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
// Create ContentObserver and listen for changes to the AUTO_TIME database field
mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
mSettingsObserver.observe(mContext);
}
Copy the code
Listen for AUTO_TIME to update the system time
Next, look at how SettingsObserver works. This requires knowledge of ContentObserver. SettingsObserver inherits ContentObserver and passes on database fields that need to be listened to,
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(); }}Copy the code
How ContentObserver works is not explained here; there will be a special article on ContentObserver later. When settings.global.auto_time changes, the onChange() method is called back by sending the message EVENT_AUTO_TIME_CHANGED. The place to receive the message and update the time is last unfolded.
Monitor network connection changes and update system time
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; }}Copy the code
When the network connection is established, the onAvailable() method is called to update the system time by calling onPollNetworkTime().
Update the system time mechanism periodically
private void registerForAlarms(a) {
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) { mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget(); }},new IntentFilter(ACTION_POLL));
}
Copy the code
Receive broadcast ACTION_POLL and send message EVENT_POLL_NETWORK_TIME to update the system time.
Updating the System 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: // Previously, the AUTO_TIME database field changed
case EVENT_POLL_NETWORK_TIME: // It is time for polling
case EVENT_NETWORK_CHANGED: // A new network connection is established
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
If the time is greater than or equal to 1 day since the last update, the system obtains the time from the NTP server again
if (mTime.getCacheAge() >= mPollingIntervalMs) {
if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
mTime.forceRefresh();
}
// If the NTP server successfully obtains the time, the timer is reset and the system time is updated
if (mTime.getCacheAge() < mPollingIntervalMs) {
// Obtained fresh fix; schedule next normal update
resetAlarm(mPollingIntervalMs);
if (isAutomaticTimeRequested()) {
updateSystemClock(event);
}
// Otherwise, enter retry
} else {
// No fresh fix; schedule retry
mTryAgainCounter++;
// If the retry times do not run out, use 60s to reset the timer
if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
resetAlarm(mPollingIntervalShorterMs);
// Otherwise, use 1 day to reset the timer
} else {
// Try much later
mTryAgainCounter = 0; resetAlarm(mPollingIntervalMs); }}}private void updateSystemClock(int event) {
// If the automatic update time is enabled by the user, force update is enabled
final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
if(! forceUpdate) {// If the system time has been updated through NITZ within a day, it will not be updated
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());
// If the difference between the NTP server time and the system time is less than 5s, the system time will not be updated
if (skew < mTimeErrorThresholdMs) {
if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
return; }}// AlarmManagerService is used to update the system time
SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
}
Copy the code
NtpTrustedTime
To take a closer look at the NTP implementation code for Android, check out the source file below.
frameworks\base\core\java\android\util\TrustedTime.java
frameworks\base\core\java\android\util\NtpTrustedTime.java
frameworks\base\core\java\android\net\SntpClient.java
Copy the code
NITZ
The NITZ code is found in the telephony module, where:
frameworks/opt/telephony
Extended learning
This article mainly shares the principle and implementation process of Android system automatically updating system time through NTP. There are some techniques that you can expand on:
- ContentObserver
- PendingIntent
- Alarm Manager/Service