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_CHANGED = 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 AlarmManager mAlarmManager;
     75     private PendingIntent mPendingPollIntent;
     76     private SettingsObserver mSettingsObserver;
     77     // The last time that we successfully fetched the NTP time.
     78     private long mLastNtpFetchTime = NOT_SET;
     79 
     80     // Normal polling frequency
     81     private final long mPollingIntervalMs;
     82     // Try-again polling interval, in case the network request failed
     83     private final long mPollingIntervalShorterMs;
     84     // Number of times to try again
     85     private final int mTryAgainTimesMax;
     86     // If the time difference is greater than this threshold, then update the time.
     87     private final int mTimeErrorThresholdMs;
     88     // Keeps track of how many quick attempts were made to fetch NTP time.
     89     // During bootup, the network may not have been up yet, or it's taking time for the
     90     // connection to happen.
     91     private int mTryAgainCounter;
     92 
     93     public NetworkTimeUpdateService(Context context) {
     94         mContext = context;
     95         mTime = NtpTrustedTime.getInstance(context);
     96         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
     97         Intent pollIntent = new Intent(ACTION_POLL, null);
     98         mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
     99 
    100         mPollingIntervalMs = mContext.getResources().getInteger(
    101                 com.android.internal.R.integer.config_ntpPollingInterval);
    102         mPollingIntervalShorterMs = mContext.getResources().getInteger(
    103                 com.android.internal.R.integer.config_ntpPollingIntervalShorter);
    104         mTryAgainTimesMax = mContext.getResources().getInteger(
    105                 com.android.internal.R.integer.config_ntpRetry);
    106         mTimeErrorThresholdMs = mContext.getResources().getInteger(
    107                 com.android.internal.R.integer.config_ntpThreshold);
    108     }
    109 
    110     /** Initialize the receivers and initiate the first NTP request */
    111     public void systemRunning() {
    112         registerForTelephonyIntents();
    113         registerForAlarms();
    114         registerForConnectivityIntents();
    115 
    116         HandlerThread thread = new HandlerThread(TAG);
    117         thread.start();
    118         mHandler = new MyHandler(thread.getLooper());
    119         // Check the network time on the new thread
    120         mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
    121 
    122         mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
    123         mSettingsObserver.observe(mContext);
    124     }
    125 
    126     private void registerForTelephonyIntents() {
    127         IntentFilter intentFilter = new IntentFilter();
    128         intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
    129         intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
    130         mContext.registerReceiver(mNitzReceiver, intentFilter);
    131     }
    132 
    133     private void registerForAlarms() {
    134         mContext.registerReceiver(
    135             new BroadcastReceiver() {
    136                 @Override
    137                 public void onReceive(Context context, Intent intent) {
    138                     mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
    139                 }
    140             }, new IntentFilter(ACTION_POLL));
    141     }
    142 
    143     private void registerForConnectivityIntents() {
    144         IntentFilter intentFilter = new IntentFilter();
    145         intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
    146         mContext.registerReceiver(mConnectivityReceiver, intentFilter);
    147     }
    148 
    149     private void onPollNetworkTime(int event) {
    150         // If Automatic time is not set, don't bother.
    151         if (!isAutomaticTimeRequested()) return;
    152 
    153         final long refTime = SystemClock.elapsedRealtime();
    154         // If NITZ time was received less than mPollingIntervalMs time ago,
    155         // no need to sync to NTP.
    156         if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
    157             resetAlarm(mPollingIntervalMs);
    158             return;
    159         }
    160         final long currentTime = System.currentTimeMillis();
    161         if (DBG) Log.d(TAG, "System time = " + currentTime);
    162         // Get the NTP time
    163         if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
    164                 || event == EVENT_AUTO_TIME_CHANGED) {
    165             if (DBG) Log.d(TAG, "Before Ntp fetch");
    166 
    167             // force refresh NTP cache when outdated
    168             if (mTime.getCacheAge() >= mPollingIntervalMs) {
    169                 mTime.forceRefresh();
    170             }
    171 
    172             // only update when NTP time is fresh
    173             if (mTime.getCacheAge() < mPollingIntervalMs) {
    174                 final long ntp = mTime.currentTimeMillis();
    175                 mTryAgainCounter = 0;
    176                 // If the clock is more than N seconds off or this is the first time it's been
    177                 // fetched since boot, set the current time.
    178                 if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
    179                         || mLastNtpFetchTime == NOT_SET) {
    180                     // Set the system time
    181                     if (DBG && mLastNtpFetchTime == NOT_SET
    182                             && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
    183                         Log.d(TAG, "For initial setup, rtc = " + currentTime);
    184                     }
    185                     if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
    186                     // Make sure we don't overflow, since it's going to be converted to an int
    187                     if (ntp / 1000 < Integer.MAX_VALUE) {
    188                         SystemClock.setCurrentTimeMillis(ntp);
    189                     }
    190                 } else {
    191                     if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
    192                 }
    193                 mLastNtpFetchTime = SystemClock.elapsedRealtime();
    194             } else {
    195                 // Try again shortly
    196                 mTryAgainCounter++;
    197                 if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
    198                     resetAlarm(mPollingIntervalShorterMs);
    199                 } else {
    200                     // Try much later
    201                     mTryAgainCounter = 0;
    202                     resetAlarm(mPollingIntervalMs);
    203                 }
    204                 return;
    205             }
    206         }
    207         resetAlarm(mPollingIntervalMs);
    208     }
    209 
    210     /**
    211      * Cancel old alarm and starts a new one for the specified interval.
    212      *
    213      * @param interval when to trigger the alarm, starting from now.
    214      */
    215     private void resetAlarm(long interval) {
    216         mAlarmManager.cancel(mPendingPollIntent);
    217         long now = SystemClock.elapsedRealtime();
    218         long next = now + interval;
    219         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
    220     }
    221 
    222     /**
    223      * Checks if the user prefers to automatically set the time.
    224      */
    225     private boolean isAutomaticTimeRequested() {
    226         return Settings.Global.getInt(
    227                 mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
    228     }
    229 
    230     /** Receiver for Nitz time events */
    231     private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
    232 
    233         @Override
    234         public void onReceive(Context context, Intent intent) {
    235             String action = intent.getAction();
    236             if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
    237                 mNitzTimeSetTime = SystemClock.elapsedRealtime();
    238             } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
    239                 mNitzZoneSetTime = SystemClock.elapsedRealtime();
    240             }
    241         }
    242     };
    243 
    244     /** Receiver for ConnectivityManager events */
    245     private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
    246 
    247         @Override
    248         public void onReceive(Context context, Intent intent) {
    249             String action = intent.getAction();
    250             if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
    251                 // Don't bother checking if we have connectivity, NtpTrustedTime does that for us.
    252                 mHandler.obtainMessage(EVENT_NETWORK_CHANGED).sendToTarget();
    253             }
    254         }
    255     };
    256 
    257     /** Handler to do the network accesses on */
    258     private class MyHandler extends Handler {
    259 
    260         public MyHandler(Looper l) {
    261             super(l);
    262         }
    263 
    264         @Override
    265         public void handleMessage(Message msg) {
    266             switch (msg.what) {
    267                 case EVENT_AUTO_TIME_CHANGED:
    268                 case EVENT_POLL_NETWORK_TIME:
    269                 case EVENT_NETWORK_CHANGED:
    270                     onPollNetworkTime(msg.what);
    271                     break;
    272             }
    273         }
    274     }
    275 
    276     /** Observer to watch for changes to the AUTO_TIME setting */
    277     private static class SettingsObserver extends ContentObserver {
    278 
    279         private int mMsg;
    280         private Handler mHandler;
    281 
    282         SettingsObserver(Handler handler, int msg) {
    283             super(handler);
    284             mHandler = handler;
    285             mMsg = msg;
    286         }
    287 
    288         void observe(Context context) {
    289             ContentResolver resolver = context.getContentResolver();
    290             resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
    291                     false, this);
    292         }
    293 
    294         @Override
    295         public void onChange(boolean selfChange) {
    296             mHandler.obtainMessage(mMsg).sendToTarget();
    297         }
    298     }
    299 }
    300