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