Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server;
     18 
     19 import android.app.AlarmManager;
     20 import android.app.PendingIntent;
     21 import android.content.BroadcastReceiver;
     22 import android.content.ContentResolver;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.database.ContentObserver;
     27 import android.net.ConnectivityManager;
     28 import android.net.NetworkInfo;
     29 import android.os.Handler;
     30 import android.os.HandlerThread;
     31 import android.os.Looper;
     32 import android.os.Message;
     33 import android.os.SystemClock;
     34 import android.provider.Settings;
     35 import android.util.Log;
     36 import android.util.NtpTrustedTime;
     37 import android.util.TrustedTime;
     38 
     39 import com.android.internal.telephony.TelephonyIntents;
     40 
     41 /**
     42  * Monitors the network time and updates the system time if it is out of sync
     43  * and there hasn't been any NITZ update from the carrier recently.
     44  * If looking up the network time fails for some reason, it tries a few times with a short
     45  * interval and then resets to checking on longer intervals.
     46  * <p>
     47  * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
     48  * available.
     49  * </p>
     50  */
     51 public class NetworkTimeUpdateService {
     52 
     53     private static final String TAG = "NetworkTimeUpdateService";
     54     private static final boolean DBG = false;
     55 
     56     private static final int EVENT_AUTO_TIME_CHANGED = 1;
     57     private static final int EVENT_POLL_NETWORK_TIME = 2;
     58     private static final int EVENT_NETWORK_CONNECTED = 3;
     59 
     60     private static final String ACTION_POLL =
     61             "com.android.server.NetworkTimeUpdateService.action.POLL";
     62     private static int POLL_REQUEST = 0;
     63 
     64     private static final long NOT_SET = -1;
     65     private long mNitzTimeSetTime = NOT_SET;
     66     // TODO: Have a way to look up the timezone we are in
     67     private long mNitzZoneSetTime = NOT_SET;
     68 
     69     private Context mContext;
     70     private TrustedTime mTime;
     71 
     72     // NTP lookup is done on this thread and handler
     73     private Handler mHandler;
     74     private HandlerThread mThread;
     75     private AlarmManager mAlarmManager;
     76     private PendingIntent mPendingPollIntent;
     77     private SettingsObserver mSettingsObserver;
     78     // The last time that we successfully fetched the NTP time.
     79     private long mLastNtpFetchTime = NOT_SET;
     80 
     81     // Normal polling frequency
     82     private final long mPollingIntervalMs;
     83     // Try-again polling interval, in case the network request failed
     84     private final long mPollingIntervalShorterMs;
     85     // Number of times to try again
     86     private final int mTryAgainTimesMax;
     87     // If the time difference is greater than this threshold, then update the time.
     88     private final int mTimeErrorThresholdMs;
     89     // Keeps track of how many quick attempts were made to fetch NTP time.
     90     // During bootup, the network may not have been up yet, or it's taking time for the
     91     // connection to happen.
     92     private int mTryAgainCounter;
     93 
     94     public NetworkTimeUpdateService(Context context) {
     95         mContext = context;
     96         mTime = NtpTrustedTime.getInstance(context);
     97         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
     98         Intent pollIntent = new Intent(ACTION_POLL, null);
     99         mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
    100 
    101         mPollingIntervalMs = mContext.getResources().getInteger(
    102                 com.android.internal.R.integer.config_ntpPollingInterval);
    103         mPollingIntervalShorterMs = mContext.getResources().getInteger(
    104                 com.android.internal.R.integer.config_ntpPollingIntervalShorter);
    105         mTryAgainTimesMax = mContext.getResources().getInteger(
    106                 com.android.internal.R.integer.config_ntpRetry);
    107         mTimeErrorThresholdMs = mContext.getResources().getInteger(
    108                 com.android.internal.R.integer.config_ntpThreshold);
    109     }
    110 
    111     /** Initialize the receivers and initiate the first NTP request */
    112     public void systemReady() {
    113         registerForTelephonyIntents();
    114         registerForAlarms();
    115         registerForConnectivityIntents();
    116 
    117         mThread = new HandlerThread(TAG);
    118         mThread.start();
    119         mHandler = new MyHandler(mThread.getLooper());
    120         // Check the network time on the new thread
    121         mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
    122 
    123         mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
    124         mSettingsObserver.observe(mContext);
    125     }
    126 
    127     private void registerForTelephonyIntents() {
    128         IntentFilter intentFilter = new IntentFilter();
    129         intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
    130         intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
    131         mContext.registerReceiver(mNitzReceiver, intentFilter);
    132     }
    133 
    134     private void registerForAlarms() {
    135         mContext.registerReceiver(
    136             new BroadcastReceiver() {
    137                 @Override
    138                 public void onReceive(Context context, Intent intent) {
    139                     mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
    140                 }
    141             }, new IntentFilter(ACTION_POLL));
    142     }
    143 
    144     private void registerForConnectivityIntents() {
    145         IntentFilter intentFilter = new IntentFilter();
    146         intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
    147         mContext.registerReceiver(mConnectivityReceiver, intentFilter);
    148     }
    149 
    150     private void onPollNetworkTime(int event) {
    151         // If Automatic time is not set, don't bother.
    152         if (!isAutomaticTimeRequested()) return;
    153 
    154         final long refTime = SystemClock.elapsedRealtime();
    155         // If NITZ time was received less than mPollingIntervalMs time ago,
    156         // no need to sync to NTP.
    157         if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
    158             resetAlarm(mPollingIntervalMs);
    159             return;
    160         }
    161         final long currentTime = System.currentTimeMillis();
    162         if (DBG) Log.d(TAG, "System time = " + currentTime);
    163         // Get the NTP time
    164         if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
    165                 || event == EVENT_AUTO_TIME_CHANGED) {
    166             if (DBG) Log.d(TAG, "Before Ntp fetch");
    167 
    168             // force refresh NTP cache when outdated
    169             if (mTime.getCacheAge() >= mPollingIntervalMs) {
    170                 mTime.forceRefresh();
    171             }
    172 
    173             // only update when NTP time is fresh
    174             if (mTime.getCacheAge() < mPollingIntervalMs) {
    175                 final long ntp = mTime.currentTimeMillis();
    176                 mTryAgainCounter = 0;
    177                 // If the clock is more than N seconds off or this is the first time it's been
    178                 // fetched since boot, set the current time.
    179                 if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
    180                         || mLastNtpFetchTime == NOT_SET) {
    181                     // Set the system time
    182                     if (DBG && mLastNtpFetchTime == NOT_SET
    183                             && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
    184                         Log.d(TAG, "For initial setup, rtc = " + currentTime);
    185                     }
    186                     if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
    187                     // Make sure we don't overflow, since it's going to be converted to an int
    188                     if (ntp / 1000 < Integer.MAX_VALUE) {
    189                         SystemClock.setCurrentTimeMillis(ntp);
    190                     }
    191                 } else {
    192                     if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
    193                 }
    194                 mLastNtpFetchTime = SystemClock.elapsedRealtime();
    195             } else {
    196                 // Try again shortly
    197                 mTryAgainCounter++;
    198                 if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
    199                     resetAlarm(mPollingIntervalShorterMs);
    200                 } else {
    201                     // Try much later
    202                     mTryAgainCounter = 0;
    203                     resetAlarm(mPollingIntervalMs);
    204                 }
    205                 return;
    206             }
    207         }
    208         resetAlarm(mPollingIntervalMs);
    209     }
    210 
    211     /**
    212      * Cancel old alarm and starts a new one for the specified interval.
    213      *
    214      * @param interval when to trigger the alarm, starting from now.
    215      */
    216     private void resetAlarm(long interval) {
    217         mAlarmManager.cancel(mPendingPollIntent);
    218         long now = SystemClock.elapsedRealtime();
    219         long next = now + interval;
    220         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
    221     }
    222 
    223     /**
    224      * Checks if the user prefers to automatically set the time.
    225      */
    226     private boolean isAutomaticTimeRequested() {
    227         return Settings.Global.getInt(
    228                 mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
    229     }
    230 
    231     /** Receiver for Nitz time events */
    232     private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
    233 
    234         @Override
    235         public void onReceive(Context context, Intent intent) {
    236             String action = intent.getAction();
    237             if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
    238                 mNitzTimeSetTime = SystemClock.elapsedRealtime();
    239             } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
    240                 mNitzZoneSetTime = SystemClock.elapsedRealtime();
    241             }
    242         }
    243     };
    244 
    245     /** Receiver for ConnectivityManager events */
    246     private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
    247 
    248         @Override
    249         public void onReceive(Context context, Intent intent) {
    250             String action = intent.getAction();
    251             if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
    252                 // There is connectivity
    253                 final ConnectivityManager connManager = (ConnectivityManager) context
    254                         .getSystemService(Context.CONNECTIVITY_SERVICE);
    255                 final NetworkInfo netInfo = connManager.getActiveNetworkInfo();
    256                 if (netInfo != null) {
    257                     // Verify that it's a WIFI connection
    258                     if (netInfo.getState() == NetworkInfo.State.CONNECTED &&
    259                             (netInfo.getType() == ConnectivityManager.TYPE_WIFI ||
    260                                 netInfo.getType() == ConnectivityManager.TYPE_ETHERNET) ) {
    261                         mHandler.obtainMessage(EVENT_NETWORK_CONNECTED).sendToTarget();
    262                     }
    263                 }
    264             }
    265         }
    266     };
    267 
    268     /** Handler to do the network accesses on */
    269     private class MyHandler extends Handler {
    270 
    271         public MyHandler(Looper l) {
    272             super(l);
    273         }
    274 
    275         @Override
    276         public void handleMessage(Message msg) {
    277             switch (msg.what) {
    278                 case EVENT_AUTO_TIME_CHANGED:
    279                 case EVENT_POLL_NETWORK_TIME:
    280                 case EVENT_NETWORK_CONNECTED:
    281                     onPollNetworkTime(msg.what);
    282                     break;
    283             }
    284         }
    285     }
    286 
    287     /** Observer to watch for changes to the AUTO_TIME setting */
    288     private static class SettingsObserver extends ContentObserver {
    289 
    290         private int mMsg;
    291         private Handler mHandler;
    292 
    293         SettingsObserver(Handler handler, int msg) {
    294             super(handler);
    295             mHandler = handler;
    296             mMsg = msg;
    297         }
    298 
    299         void observe(Context context) {
    300             ContentResolver resolver = context.getContentResolver();
    301             resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
    302                     false, this);
    303         }
    304 
    305         @Override
    306         public void onChange(boolean selfChange) {
    307             mHandler.obtainMessage(mMsg).sendToTarget();
    308         }
    309     }
    310 }
    311