summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android/server/NetworkTimeUpdateService.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/core/java/com/android/server/NetworkTimeUpdateService.java')
-rw-r--r--services/core/java/com/android/server/NetworkTimeUpdateService.java309
1 files changed, 309 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
new file mode 100644
index 0000000..fddb54e
--- /dev/null
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.NtpTrustedTime;
+import android.util.TrustedTime;
+
+import com.android.internal.telephony.TelephonyIntents;
+
+/**
+ * Monitors the network time and updates the system time if it is out of sync
+ * and there hasn't been any NITZ update from the carrier recently.
+ * If looking up the network time fails for some reason, it tries a few times with a short
+ * interval and then resets to checking on longer intervals.
+ * <p>
+ * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
+ * available.
+ * </p>
+ */
+public class NetworkTimeUpdateService {
+
+ private static final String TAG = "NetworkTimeUpdateService";
+ private static final boolean DBG = false;
+
+ private static final int EVENT_AUTO_TIME_CHANGED = 1;
+ private static final int EVENT_POLL_NETWORK_TIME = 2;
+ private static final int EVENT_NETWORK_CONNECTED = 3;
+
+ private static final String ACTION_POLL =
+ "com.android.server.NetworkTimeUpdateService.action.POLL";
+ private static int POLL_REQUEST = 0;
+
+ private static final long NOT_SET = -1;
+ private long mNitzTimeSetTime = NOT_SET;
+ // TODO: Have a way to look up the timezone we are in
+ private long mNitzZoneSetTime = NOT_SET;
+
+ private Context mContext;
+ private TrustedTime mTime;
+
+ // NTP lookup is done on this thread and handler
+ private Handler mHandler;
+ private AlarmManager mAlarmManager;
+ private PendingIntent mPendingPollIntent;
+ private SettingsObserver mSettingsObserver;
+ // The last time that we successfully fetched the NTP time.
+ private long mLastNtpFetchTime = NOT_SET;
+
+ // Normal polling frequency
+ private final long mPollingIntervalMs;
+ // Try-again polling interval, in case the network request failed
+ private final long mPollingIntervalShorterMs;
+ // Number of times to try again
+ private final int mTryAgainTimesMax;
+ // If the time difference is greater than this threshold, then update the time.
+ private final int mTimeErrorThresholdMs;
+ // Keeps track of how many quick attempts were made to fetch NTP time.
+ // During bootup, the network may not have been up yet, or it's taking time for the
+ // connection to happen.
+ private int mTryAgainCounter;
+
+ public NetworkTimeUpdateService(Context context) {
+ mContext = context;
+ mTime = NtpTrustedTime.getInstance(context);
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ Intent pollIntent = new Intent(ACTION_POLL, null);
+ mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
+
+ 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);
+ mTimeErrorThresholdMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpThreshold);
+ }
+
+ /** Initialize the receivers and initiate the first NTP request */
+ public void systemRunning() {
+ registerForTelephonyIntents();
+ registerForAlarms();
+ registerForConnectivityIntents();
+
+ HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ mHandler = new MyHandler(thread.getLooper());
+ // Check the network time on the new thread
+ mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+
+ mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
+ mSettingsObserver.observe(mContext);
+ }
+
+ private void registerForTelephonyIntents() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+ intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
+ mContext.registerReceiver(mNitzReceiver, intentFilter);
+ }
+
+ 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));
+ }
+
+ private void registerForConnectivityIntents() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ mContext.registerReceiver(mConnectivityReceiver, intentFilter);
+ }
+
+ private void onPollNetworkTime(int event) {
+ // If Automatic time is not set, don't bother.
+ if (!isAutomaticTimeRequested()) return;
+
+ final long refTime = SystemClock.elapsedRealtime();
+ // If NITZ time was received less than mPollingIntervalMs time ago,
+ // no need to sync to NTP.
+ if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
+ resetAlarm(mPollingIntervalMs);
+ return;
+ }
+ final long currentTime = System.currentTimeMillis();
+ if (DBG) Log.d(TAG, "System time = " + currentTime);
+ // Get the NTP time
+ if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
+ || event == EVENT_AUTO_TIME_CHANGED) {
+ if (DBG) Log.d(TAG, "Before Ntp fetch");
+
+ // force refresh NTP cache when outdated
+ if (mTime.getCacheAge() >= mPollingIntervalMs) {
+ mTime.forceRefresh();
+ }
+
+ // only update when NTP time is fresh
+ if (mTime.getCacheAge() < mPollingIntervalMs) {
+ final long ntp = mTime.currentTimeMillis();
+ mTryAgainCounter = 0;
+ // If the clock is more than N seconds off or this is the first time it's been
+ // fetched since boot, set the current time.
+ if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
+ || mLastNtpFetchTime == NOT_SET) {
+ // Set the system time
+ if (DBG && mLastNtpFetchTime == NOT_SET
+ && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
+ Log.d(TAG, "For initial setup, rtc = " + currentTime);
+ }
+ if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
+ // Make sure we don't overflow, since it's going to be converted to an int
+ if (ntp / 1000 < Integer.MAX_VALUE) {
+ SystemClock.setCurrentTimeMillis(ntp);
+ }
+ } else {
+ if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
+ }
+ mLastNtpFetchTime = SystemClock.elapsedRealtime();
+ } else {
+ // Try again shortly
+ mTryAgainCounter++;
+ if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
+ resetAlarm(mPollingIntervalShorterMs);
+ } else {
+ // Try much later
+ mTryAgainCounter = 0;
+ resetAlarm(mPollingIntervalMs);
+ }
+ return;
+ }
+ }
+ resetAlarm(mPollingIntervalMs);
+ }
+
+ /**
+ * Cancel old alarm and starts a new one for the specified interval.
+ *
+ * @param interval when to trigger the alarm, starting from now.
+ */
+ private void resetAlarm(long interval) {
+ mAlarmManager.cancel(mPendingPollIntent);
+ long now = SystemClock.elapsedRealtime();
+ long next = now + interval;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
+ }
+
+ /**
+ * Checks if the user prefers to automatically set the time.
+ */
+ private boolean isAutomaticTimeRequested() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
+ }
+
+ /** Receiver for Nitz time events */
+ private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
+ mNitzTimeSetTime = SystemClock.elapsedRealtime();
+ } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
+ mNitzZoneSetTime = SystemClock.elapsedRealtime();
+ }
+ }
+ };
+
+ /** Receiver for ConnectivityManager events */
+ private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
+ // There is connectivity
+ final ConnectivityManager connManager = (ConnectivityManager) context
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ final NetworkInfo netInfo = connManager.getActiveNetworkInfo();
+ if (netInfo != null) {
+ // Verify that it's a WIFI connection
+ if (netInfo.getState() == NetworkInfo.State.CONNECTED &&
+ (netInfo.getType() == ConnectivityManager.TYPE_WIFI ||
+ netInfo.getType() == ConnectivityManager.TYPE_ETHERNET) ) {
+ mHandler.obtainMessage(EVENT_NETWORK_CONNECTED).sendToTarget();
+ }
+ }
+ }
+ }
+ };
+
+ /** 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:
+ case EVENT_POLL_NETWORK_TIME:
+ case EVENT_NETWORK_CONNECTED:
+ onPollNetworkTime(msg.what);
+ break;
+ }
+ }
+ }
+
+ /** 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();
+ }
+ }
+}