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.content.pm.PackageManager;
     27 import android.database.ContentObserver;
     28 import android.net.ConnectivityManager;
     29 import android.os.Binder;
     30 import android.os.Handler;
     31 import android.os.HandlerThread;
     32 import android.os.Looper;
     33 import android.os.Message;
     34 import android.os.SystemClock;
     35 import android.os.PowerManager;
     36 import android.provider.Settings;
     37 import android.util.Log;
     38 import android.util.NtpTrustedTime;
     39 import android.util.TimeUtils;
     40 import android.util.TrustedTime;
     41 
     42 import com.android.internal.telephony.TelephonyIntents;
     43 import com.android.internal.util.DumpUtils;
     44 
     45 import java.io.FileDescriptor;
     46 import java.io.PrintWriter;
     47 
     48 /**
     49  * Monitors the network time and updates the system time if it is out of sync
     50  * and there hasn't been any NITZ update from the carrier recently.
     51  * If looking up the network time fails for some reason, it tries a few times with a short
     52  * interval and then resets to checking on longer intervals.
     53  * <p>
     54  * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
     55  * available.
     56  * </p>
     57  */
     58 public class NetworkTimeUpdateService extends Binder {
     59 
     60     private static final String TAG = "NetworkTimeUpdateService";
     61     private static final boolean DBG = false;
     62 
     63     private static final int EVENT_AUTO_TIME_CHANGED = 1;
     64     private static final int EVENT_POLL_NETWORK_TIME = 2;
     65     private static final int EVENT_NETWORK_CHANGED = 3;
     66 
     67     private static final String ACTION_POLL =
     68             "com.android.server.NetworkTimeUpdateService.action.POLL";
     69 
     70     private static final int NETWORK_CHANGE_EVENT_DELAY_MS = 1000;
     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 AlarmManager mAlarmManager;
     84     private PendingIntent mPendingPollIntent;
     85     private SettingsObserver mSettingsObserver;
     86     // The last time that we successfully fetched the NTP time.
     87     private long mLastNtpFetchTime = NOT_SET;
     88     private final PowerManager.WakeLock mWakeLock;
     89 
     90     // Normal polling frequency
     91     private final long mPollingIntervalMs;
     92     // Try-again polling interval, in case the network request failed
     93     private final long mPollingIntervalShorterMs;
     94     // Number of times to try again
     95     private final int mTryAgainTimesMax;
     96     // If the time difference is greater than this threshold, then update the time.
     97     private final int mTimeErrorThresholdMs;
     98     // Keeps track of how many quick attempts were made to fetch NTP time.
     99     // During bootup, the network may not have been up yet, or it's taking time for the
    100     // connection to happen.
    101     private int mTryAgainCounter;
    102 
    103     public NetworkTimeUpdateService(Context context) {
    104         mContext = context;
    105         mTime = NtpTrustedTime.getInstance(context);
    106         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
    107         Intent pollIntent = new Intent(ACTION_POLL, null);
    108         mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
    109 
    110         mPollingIntervalMs = mContext.getResources().getInteger(
    111                 com.android.internal.R.integer.config_ntpPollingInterval);
    112         mPollingIntervalShorterMs = mContext.getResources().getInteger(
    113                 com.android.internal.R.integer.config_ntpPollingIntervalShorter);
    114         mTryAgainTimesMax = mContext.getResources().getInteger(
    115                 com.android.internal.R.integer.config_ntpRetry);
    116         mTimeErrorThresholdMs = mContext.getResources().getInteger(
    117                 com.android.internal.R.integer.config_ntpThreshold);
    118 
    119         mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
    120                 PowerManager.PARTIAL_WAKE_LOCK, TAG);
    121     }
    122 
    123     /** Initialize the receivers and initiate the first NTP request */
    124     public void systemRunning() {
    125         registerForTelephonyIntents();
    126         registerForAlarms();
    127         registerForConnectivityIntents();
    128 
    129         HandlerThread thread = new HandlerThread(TAG);
    130         thread.start();
    131         mHandler = new MyHandler(thread.getLooper());
    132         // Check the network time on the new thread
    133         mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
    134 
    135         mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
    136         mSettingsObserver.observe(mContext);
    137     }
    138 
    139     private void registerForTelephonyIntents() {
    140         IntentFilter intentFilter = new IntentFilter();
    141         intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
    142         intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
    143         mContext.registerReceiver(mNitzReceiver, intentFilter);
    144     }
    145 
    146     private void registerForAlarms() {
    147         mContext.registerReceiver(
    148             new BroadcastReceiver() {
    149                 @Override
    150                 public void onReceive(Context context, Intent intent) {
    151                     mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
    152                 }
    153             }, new IntentFilter(ACTION_POLL));
    154     }
    155 
    156     private void registerForConnectivityIntents() {
    157         IntentFilter intentFilter = new IntentFilter();
    158         intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
    159         mContext.registerReceiver(mConnectivityReceiver, intentFilter);
    160     }
    161 
    162     private void onPollNetworkTime(int event) {
    163         // If Automatic time is not set, don't bother.
    164         if (!isAutomaticTimeRequested()) return;
    165         mWakeLock.acquire();
    166         try {
    167             onPollNetworkTimeUnderWakeLock(event);
    168         } finally {
    169             mWakeLock.release();
    170         }
    171     }
    172 
    173     private void onPollNetworkTimeUnderWakeLock(int event) {
    174         final long refTime = SystemClock.elapsedRealtime();
    175         // If NITZ time was received less than mPollingIntervalMs time ago,
    176         // no need to sync to NTP.
    177         if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
    178             resetAlarm(mPollingIntervalMs);
    179             return;
    180         }
    181         final long currentTime = System.currentTimeMillis();
    182         if (DBG) Log.d(TAG, "System time = " + currentTime);
    183         // Get the NTP time
    184         if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
    185                 || event == EVENT_AUTO_TIME_CHANGED) {
    186             if (DBG) Log.d(TAG, "Before Ntp fetch");
    187 
    188             // force refresh NTP cache when outdated
    189             if (mTime.getCacheAge() >= mPollingIntervalMs) {
    190                 mTime.forceRefresh();
    191             }
    192 
    193             // only update when NTP time is fresh
    194             if (mTime.getCacheAge() < mPollingIntervalMs) {
    195                 final long ntp = mTime.currentTimeMillis();
    196                 mTryAgainCounter = 0;
    197                 // If the clock is more than N seconds off or this is the first time it's been
    198                 // fetched since boot, set the current time.
    199                 if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
    200                         || mLastNtpFetchTime == NOT_SET) {
    201                     // Set the system time
    202                     if (DBG && mLastNtpFetchTime == NOT_SET
    203                             && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
    204                         Log.d(TAG, "For initial setup, rtc = " + currentTime);
    205                     }
    206                     if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
    207                     // Make sure we don't overflow, since it's going to be converted to an int
    208                     if (ntp / 1000 < Integer.MAX_VALUE) {
    209                         SystemClock.setCurrentTimeMillis(ntp);
    210                     }
    211                 } else {
    212                     if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
    213                 }
    214                 mLastNtpFetchTime = SystemClock.elapsedRealtime();
    215             } else {
    216                 // Try again shortly
    217                 mTryAgainCounter++;
    218                 if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
    219                     resetAlarm(mPollingIntervalShorterMs);
    220                 } else {
    221                     // Try much later
    222                     mTryAgainCounter = 0;
    223                     resetAlarm(mPollingIntervalMs);
    224                 }
    225                 return;
    226             }
    227         }
    228         resetAlarm(mPollingIntervalMs);
    229     }
    230 
    231     /**
    232      * Cancel old alarm and starts a new one for the specified interval.
    233      *
    234      * @param interval when to trigger the alarm, starting from now.
    235      */
    236     private void resetAlarm(long interval) {
    237         mAlarmManager.cancel(mPendingPollIntent);
    238         long now = SystemClock.elapsedRealtime();
    239         long next = now + interval;
    240         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
    241     }
    242 
    243     /**
    244      * Checks if the user prefers to automatically set the time.
    245      */
    246     private boolean isAutomaticTimeRequested() {
    247         return Settings.Global.getInt(
    248                 mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
    249     }
    250 
    251     /** Receiver for Nitz time events */
    252     private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
    253 
    254         @Override
    255         public void onReceive(Context context, Intent intent) {
    256             String action = intent.getAction();
    257             if (DBG) Log.d(TAG, "Received " + action);
    258             if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
    259                 mNitzTimeSetTime = SystemClock.elapsedRealtime();
    260             } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
    261                 mNitzZoneSetTime = SystemClock.elapsedRealtime();
    262             }
    263         }
    264     };
    265 
    266     /** Receiver for ConnectivityManager events */
    267     private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
    268 
    269         @Override
    270         public void onReceive(Context context, Intent intent) {
    271             String action = intent.getAction();
    272             if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
    273                 if (DBG) Log.d(TAG, "Received CONNECTIVITY_ACTION ");
    274                 // Don't bother checking if we have connectivity, NtpTrustedTime does that for us.
    275                 Message message = mHandler.obtainMessage(EVENT_NETWORK_CHANGED);
    276                 // Send with a short delay to make sure the network is ready for use
    277                 mHandler.sendMessageDelayed(message, NETWORK_CHANGE_EVENT_DELAY_MS);
    278             }
    279         }
    280     };
    281 
    282     /** Handler to do the network accesses on */
    283     private class MyHandler extends Handler {
    284 
    285         public MyHandler(Looper l) {
    286             super(l);
    287         }
    288 
    289         @Override
    290         public void handleMessage(Message msg) {
    291             switch (msg.what) {
    292                 case EVENT_AUTO_TIME_CHANGED:
    293                 case EVENT_POLL_NETWORK_TIME:
    294                 case EVENT_NETWORK_CHANGED:
    295                     onPollNetworkTime(msg.what);
    296                     break;
    297             }
    298         }
    299     }
    300 
    301     /** Observer to watch for changes to the AUTO_TIME setting */
    302     private static class SettingsObserver extends ContentObserver {
    303 
    304         private int mMsg;
    305         private Handler mHandler;
    306 
    307         SettingsObserver(Handler handler, int msg) {
    308             super(handler);
    309             mHandler = handler;
    310             mMsg = msg;
    311         }
    312 
    313         void observe(Context context) {
    314             ContentResolver resolver = context.getContentResolver();
    315             resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
    316                     false, this);
    317         }
    318 
    319         @Override
    320         public void onChange(boolean selfChange) {
    321             mHandler.obtainMessage(mMsg).sendToTarget();
    322         }
    323     }
    324 
    325     @Override
    326     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    327         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
    328         pw.print("PollingIntervalMs: ");
    329         TimeUtils.formatDuration(mPollingIntervalMs, pw);
    330         pw.print("\nPollingIntervalShorterMs: ");
    331         TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
    332         pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
    333         pw.print("TimeErrorThresholdMs: ");
    334         TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
    335         pw.println("\nTryAgainCounter: " + mTryAgainCounter);
    336         pw.print("LastNtpFetchTime: ");
    337         TimeUtils.formatDuration(mLastNtpFetchTime, pw);
    338         pw.println();
    339     }
    340 }
    341