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.ConnectivityManager.NetworkCallback;
     29 import android.net.Network;
     30 import android.os.Binder;
     31 import android.os.Handler;
     32 import android.os.HandlerThread;
     33 import android.os.Looper;
     34 import android.os.Message;
     35 import android.os.PowerManager;
     36 import android.os.SystemClock;
     37 import android.provider.Settings;
     38 import android.util.Log;
     39 import android.util.NtpTrustedTime;
     40 import android.util.TimeUtils;
     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 POLL_REQUEST = 0;
     71 
     72     private static final long NOT_SET = -1;
     73     private long mNitzTimeSetTime = NOT_SET;
     74     private Network mDefaultNetwork = null;
     75 
     76     private final Context mContext;
     77     private final NtpTrustedTime mTime;
     78     private final AlarmManager mAlarmManager;
     79     private final ConnectivityManager mCM;
     80     private final PendingIntent mPendingPollIntent;
     81     private final PowerManager.WakeLock mWakeLock;
     82 
     83     // NTP lookup is done on this thread and handler
     84     private Handler mHandler;
     85     private SettingsObserver mSettingsObserver;
     86     private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
     87 
     88     // Normal polling frequency
     89     private final long mPollingIntervalMs;
     90     // Try-again polling interval, in case the network request failed
     91     private final long mPollingIntervalShorterMs;
     92     // Number of times to try again
     93     private final int mTryAgainTimesMax;
     94     // If the time difference is greater than this threshold, then update the time.
     95     private final int mTimeErrorThresholdMs;
     96     // Keeps track of how many quick attempts were made to fetch NTP time.
     97     // During bootup, the network may not have been up yet, or it's taking time for the
     98     // connection to happen.
     99     private int mTryAgainCounter;
    100 
    101     public NetworkTimeUpdateService(Context context) {
    102         mContext = context;
    103         mTime = NtpTrustedTime.getInstance(context);
    104         mAlarmManager = mContext.getSystemService(AlarmManager.class);
    105         mCM = mContext.getSystemService(ConnectivityManager.class);
    106 
    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 = context.getSystemService(PowerManager.class).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 
    128         HandlerThread thread = new HandlerThread(TAG);
    129         thread.start();
    130         mHandler = new MyHandler(thread.getLooper());
    131         mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
    132         mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
    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         mContext.registerReceiver(mNitzReceiver, intentFilter);
    142     }
    143 
    144     private void registerForAlarms() {
    145         mContext.registerReceiver(
    146             new BroadcastReceiver() {
    147                 @Override
    148                 public void onReceive(Context context, Intent intent) {
    149                     mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
    150                 }
    151             }, new IntentFilter(ACTION_POLL));
    152     }
    153 
    154     private void onPollNetworkTime(int event) {
    155         // If Automatic time is not set, don't bother. Similarly, if we don't
    156         // have any default network, don't bother.
    157         if (mDefaultNetwork == null) return;
    158         mWakeLock.acquire();
    159         try {
    160             onPollNetworkTimeUnderWakeLock(event);
    161         } finally {
    162             mWakeLock.release();
    163         }
    164     }
    165 
    166     private void onPollNetworkTimeUnderWakeLock(int event) {
    167         // Force an NTP fix when outdated
    168         if (mTime.getCacheAge() >= mPollingIntervalMs) {
    169             if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
    170             mTime.forceRefresh();
    171         }
    172 
    173         if (mTime.getCacheAge() < mPollingIntervalMs) {
    174             // Obtained fresh fix; schedule next normal update
    175             resetAlarm(mPollingIntervalMs);
    176             if (isAutomaticTimeRequested()) {
    177                 updateSystemClock(event);
    178             }
    179 
    180         } else {
    181             // No fresh fix; schedule retry
    182             mTryAgainCounter++;
    183             if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
    184                 resetAlarm(mPollingIntervalShorterMs);
    185             } else {
    186                 // Try much later
    187                 mTryAgainCounter = 0;
    188                 resetAlarm(mPollingIntervalMs);
    189             }
    190         }
    191     }
    192 
    193     private long getNitzAge() {
    194         if (mNitzTimeSetTime == NOT_SET) {
    195             return Long.MAX_VALUE;
    196         } else {
    197             return SystemClock.elapsedRealtime() - mNitzTimeSetTime;
    198         }
    199     }
    200 
    201     /**
    202      * Consider updating system clock based on current NTP fix, if requested by
    203      * user, significant enough delta, and we don't have a recent NITZ.
    204      */
    205     private void updateSystemClock(int event) {
    206         final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
    207         if (!forceUpdate) {
    208             if (getNitzAge() < mPollingIntervalMs) {
    209                 if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
    210                 return;
    211             }
    212 
    213             final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
    214             if (skew < mTimeErrorThresholdMs) {
    215                 if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
    216                 return;
    217             }
    218         }
    219 
    220         SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
    221     }
    222 
    223     /**
    224      * Cancel old alarm and starts a new one for the specified interval.
    225      *
    226      * @param interval when to trigger the alarm, starting from now.
    227      */
    228     private void resetAlarm(long interval) {
    229         mAlarmManager.cancel(mPendingPollIntent);
    230         long now = SystemClock.elapsedRealtime();
    231         long next = now + interval;
    232         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
    233     }
    234 
    235     /**
    236      * Checks if the user prefers to automatically set the time.
    237      */
    238     private boolean isAutomaticTimeRequested() {
    239         return Settings.Global.getInt(
    240                 mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
    241     }
    242 
    243     /** Receiver for Nitz time events */
    244     private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
    245 
    246         @Override
    247         public void onReceive(Context context, Intent intent) {
    248             String action = intent.getAction();
    249             if (DBG) Log.d(TAG, "Received " + action);
    250             if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
    251                 mNitzTimeSetTime = SystemClock.elapsedRealtime();
    252             }
    253         }
    254     };
    255 
    256     /** Handler to do the network accesses on */
    257     private class MyHandler extends Handler {
    258 
    259         public MyHandler(Looper l) {
    260             super(l);
    261         }
    262 
    263         @Override
    264         public void handleMessage(Message msg) {
    265             switch (msg.what) {
    266                 case EVENT_AUTO_TIME_CHANGED:
    267                 case EVENT_POLL_NETWORK_TIME:
    268                 case EVENT_NETWORK_CHANGED:
    269                     onPollNetworkTime(msg.what);
    270                     break;
    271             }
    272         }
    273     }
    274 
    275     private class NetworkTimeUpdateCallback extends NetworkCallback {
    276         @Override
    277         public void onAvailable(Network network) {
    278             Log.d(TAG, String.format("New default network %s; checking time.", network));
    279             mDefaultNetwork = network;
    280             // Running on mHandler so invoke directly.
    281             onPollNetworkTime(EVENT_NETWORK_CHANGED);
    282         }
    283 
    284         @Override
    285         public void onLost(Network network) {
    286             if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
    287         }
    288     }
    289 
    290     /** Observer to watch for changes to the AUTO_TIME setting */
    291     private static class SettingsObserver extends ContentObserver {
    292 
    293         private int mMsg;
    294         private Handler mHandler;
    295 
    296         SettingsObserver(Handler handler, int msg) {
    297             super(handler);
    298             mHandler = handler;
    299             mMsg = msg;
    300         }
    301 
    302         void observe(Context context) {
    303             ContentResolver resolver = context.getContentResolver();
    304             resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
    305                     false, this);
    306         }
    307 
    308         @Override
    309         public void onChange(boolean selfChange) {
    310             mHandler.obtainMessage(mMsg).sendToTarget();
    311         }
    312     }
    313 
    314     @Override
    315     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    316         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
    317         pw.print("PollingIntervalMs: ");
    318         TimeUtils.formatDuration(mPollingIntervalMs, pw);
    319         pw.print("\nPollingIntervalShorterMs: ");
    320         TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
    321         pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
    322         pw.print("TimeErrorThresholdMs: ");
    323         TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
    324         pw.println("\nTryAgainCounter: " + mTryAgainCounter);
    325         pw.println("NTP cache age: " + mTime.getCacheAge());
    326         pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
    327         pw.println();
    328     }
    329 }
    330