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_WIFI_CONNECTED = 3;
     59 
     60     /** Normal polling frequency */
     61     private static final long POLLING_INTERVAL_MS = 24L * 60 * 60 * 1000; // 24 hrs
     62     /** Try-again polling interval, in case the network request failed */
     63     private static final long POLLING_INTERVAL_SHORTER_MS = 60 * 1000L; // 60 seconds
     64     /** Number of times to try again */
     65     private static final int TRY_AGAIN_TIMES_MAX = 3;
     66     /** If the time difference is greater than this threshold, then update the time. */
     67     private static final int TIME_ERROR_THRESHOLD_MS = 5 * 1000;
     68 
     69     private static final String ACTION_POLL =
     70             "com.android.server.NetworkTimeUpdateService.action.POLL";
     71     private static int POLL_REQUEST = 0;
     72 
     73     private static final long NOT_SET = -1;
     74     private long mNitzTimeSetTime = NOT_SET;
     75     // TODO: Have a way to look up the timezone we are in
     76     private long mNitzZoneSetTime = NOT_SET;
     77 
     78     private Context mContext;
     79     private TrustedTime mTime;
     80 
     81     // NTP lookup is done on this thread and handler
     82     private Handler mHandler;
     83     private HandlerThread mThread;
     84     private AlarmManager mAlarmManager;
     85     private PendingIntent mPendingPollIntent;
     86     private SettingsObserver mSettingsObserver;
     87     // The last time that we successfully fetched the NTP time.
     88     private long mLastNtpFetchTime = NOT_SET;
     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 
    102     /** Initialize the receivers and initiate the first NTP request */
    103     public void systemReady() {
    104         registerForTelephonyIntents();
    105         registerForAlarms();
    106         registerForConnectivityIntents();
    107 
    108         mThread = new HandlerThread(TAG);
    109         mThread.start();
    110         mHandler = new MyHandler(mThread.getLooper());
    111         // Check the network time on the new thread
    112         mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
    113 
    114         mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
    115         mSettingsObserver.observe(mContext);
    116     }
    117 
    118     private void registerForTelephonyIntents() {
    119         IntentFilter intentFilter = new IntentFilter();
    120         intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
    121         intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
    122         mContext.registerReceiver(mNitzReceiver, intentFilter);
    123     }
    124 
    125     private void registerForAlarms() {
    126         mContext.registerReceiver(
    127             new BroadcastReceiver() {
    128                 @Override
    129                 public void onReceive(Context context, Intent intent) {
    130                     mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
    131                 }
    132             }, new IntentFilter(ACTION_POLL));
    133     }
    134 
    135     private void registerForConnectivityIntents() {
    136         IntentFilter intentFilter = new IntentFilter();
    137         intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
    138         mContext.registerReceiver(mConnectivityReceiver, intentFilter);
    139     }
    140 
    141     private void onPollNetworkTime(int event) {
    142         // If Automatic time is not set, don't bother.
    143         if (!isAutomaticTimeRequested()) return;
    144 
    145         final long refTime = SystemClock.elapsedRealtime();
    146         // If NITZ time was received less than POLLING_INTERVAL_MS time ago,
    147         // no need to sync to NTP.
    148         if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < POLLING_INTERVAL_MS) {
    149             resetAlarm(POLLING_INTERVAL_MS);
    150             return;
    151         }
    152         final long currentTime = System.currentTimeMillis();
    153         if (DBG) Log.d(TAG, "System time = " + currentTime);
    154         // Get the NTP time
    155         if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + POLLING_INTERVAL_MS
    156                 || event == EVENT_AUTO_TIME_CHANGED) {
    157             if (DBG) Log.d(TAG, "Before Ntp fetch");
    158 
    159             // force refresh NTP cache when outdated
    160             if (mTime.getCacheAge() >= POLLING_INTERVAL_MS) {
    161                 mTime.forceRefresh();
    162             }
    163 
    164             // only update when NTP time is fresh
    165             if (mTime.getCacheAge() < POLLING_INTERVAL_MS) {
    166                 final long ntp = mTime.currentTimeMillis();
    167                 mTryAgainCounter = 0;
    168                 mLastNtpFetchTime = SystemClock.elapsedRealtime();
    169                 if (Math.abs(ntp - currentTime) > TIME_ERROR_THRESHOLD_MS) {
    170                     // Set the system time
    171                     if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
    172                     // Make sure we don't overflow, since it's going to be converted to an int
    173                     if (ntp / 1000 < Integer.MAX_VALUE) {
    174                         SystemClock.setCurrentTimeMillis(ntp);
    175                     }
    176                 } else {
    177                     if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
    178                 }
    179             } else {
    180                 // Try again shortly
    181                 mTryAgainCounter++;
    182                 if (mTryAgainCounter <= TRY_AGAIN_TIMES_MAX) {
    183                     resetAlarm(POLLING_INTERVAL_SHORTER_MS);
    184                 } else {
    185                     // Try much later
    186                     mTryAgainCounter = 0;
    187                     resetAlarm(POLLING_INTERVAL_MS);
    188                 }
    189                 return;
    190             }
    191         }
    192         resetAlarm(POLLING_INTERVAL_MS);
    193     }
    194 
    195     /**
    196      * Cancel old alarm and starts a new one for the specified interval.
    197      *
    198      * @param interval when to trigger the alarm, starting from now.
    199      */
    200     private void resetAlarm(long interval) {
    201         mAlarmManager.cancel(mPendingPollIntent);
    202         long now = SystemClock.elapsedRealtime();
    203         long next = now + interval;
    204         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
    205     }
    206 
    207     /**
    208      * Checks if the user prefers to automatically set the time.
    209      */
    210     private boolean isAutomaticTimeRequested() {
    211         return Settings.System.getInt(mContext.getContentResolver(), Settings.System.AUTO_TIME, 0)
    212                 != 0;
    213     }
    214 
    215     /** Receiver for Nitz time events */
    216     private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
    217 
    218         @Override
    219         public void onReceive(Context context, Intent intent) {
    220             String action = intent.getAction();
    221             if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
    222                 mNitzTimeSetTime = SystemClock.elapsedRealtime();
    223             } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
    224                 mNitzZoneSetTime = SystemClock.elapsedRealtime();
    225             }
    226         }
    227     };
    228 
    229     /** Receiver for ConnectivityManager events */
    230     private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
    231 
    232         @Override
    233         public void onReceive(Context context, Intent intent) {
    234             String action = intent.getAction();
    235             if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
    236                 // There is connectivity
    237                 NetworkInfo netInfo = (NetworkInfo)intent.getParcelableExtra(
    238                         ConnectivityManager.EXTRA_NETWORK_INFO);
    239                 if (netInfo != null) {
    240                     // Verify that it's a WIFI connection
    241                     if (netInfo.getState() == NetworkInfo.State.CONNECTED &&
    242                             netInfo.getType() == ConnectivityManager.TYPE_WIFI ) {
    243                         mHandler.obtainMessage(EVENT_WIFI_CONNECTED).sendToTarget();
    244                     }
    245                 }
    246             }
    247         }
    248     };
    249 
    250     /** Handler to do the network accesses on */
    251     private class MyHandler extends Handler {
    252 
    253         public MyHandler(Looper l) {
    254             super(l);
    255         }
    256 
    257         @Override
    258         public void handleMessage(Message msg) {
    259             switch (msg.what) {
    260                 case EVENT_AUTO_TIME_CHANGED:
    261                 case EVENT_POLL_NETWORK_TIME:
    262                 case EVENT_WIFI_CONNECTED:
    263                     onPollNetworkTime(msg.what);
    264                     break;
    265             }
    266         }
    267     }
    268 
    269     /** Observer to watch for changes to the AUTO_TIME setting */
    270     private static class SettingsObserver extends ContentObserver {
    271 
    272         private int mMsg;
    273         private Handler mHandler;
    274 
    275         SettingsObserver(Handler handler, int msg) {
    276             super(handler);
    277             mHandler = handler;
    278             mMsg = msg;
    279         }
    280 
    281         void observe(Context context) {
    282             ContentResolver resolver = context.getContentResolver();
    283             resolver.registerContentObserver(Settings.System.getUriFor(Settings.System.AUTO_TIME),
    284                     false, this);
    285         }
    286 
    287         @Override
    288         public void onChange(boolean selfChange) {
    289             mHandler.obtainMessage(mMsg).sendToTarget();
    290         }
    291     }
    292 }
    293