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
17package com.android.server;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.content.BroadcastReceiver;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.database.ContentObserver;
27import android.net.ConnectivityManager;
28import android.net.NetworkInfo;
29import android.os.Handler;
30import android.os.HandlerThread;
31import android.os.Looper;
32import android.os.Message;
33import android.os.SystemClock;
34import android.provider.Settings;
35import android.util.Log;
36import android.util.NtpTrustedTime;
37import android.util.TrustedTime;
38
39import 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 */
51public 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_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                // If the clock is more than N seconds off or this is the first time it's been
169                // fetched since boot, set the current time.
170                if (Math.abs(ntp - currentTime) > TIME_ERROR_THRESHOLD_MS
171                        || mLastNtpFetchTime == NOT_SET) {
172                    // Set the system time
173                    if (DBG && mLastNtpFetchTime == NOT_SET
174                            && Math.abs(ntp - currentTime) <= TIME_ERROR_THRESHOLD_MS) {
175                        Log.d(TAG, "For initial setup, rtc = " + currentTime);
176                    }
177                    if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
178                    // Make sure we don't overflow, since it's going to be converted to an int
179                    if (ntp / 1000 < Integer.MAX_VALUE) {
180                        SystemClock.setCurrentTimeMillis(ntp);
181                    }
182                } else {
183                    if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
184                }
185                mLastNtpFetchTime = SystemClock.elapsedRealtime();
186            } else {
187                // Try again shortly
188                mTryAgainCounter++;
189                if (mTryAgainCounter <= TRY_AGAIN_TIMES_MAX) {
190                    resetAlarm(POLLING_INTERVAL_SHORTER_MS);
191                } else {
192                    // Try much later
193                    mTryAgainCounter = 0;
194                    resetAlarm(POLLING_INTERVAL_MS);
195                }
196                return;
197            }
198        }
199        resetAlarm(POLLING_INTERVAL_MS);
200    }
201
202    /**
203     * Cancel old alarm and starts a new one for the specified interval.
204     *
205     * @param interval when to trigger the alarm, starting from now.
206     */
207    private void resetAlarm(long interval) {
208        mAlarmManager.cancel(mPendingPollIntent);
209        long now = SystemClock.elapsedRealtime();
210        long next = now + interval;
211        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
212    }
213
214    /**
215     * Checks if the user prefers to automatically set the time.
216     */
217    private boolean isAutomaticTimeRequested() {
218        return Settings.Global.getInt(
219                mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
220    }
221
222    /** Receiver for Nitz time events */
223    private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
224
225        @Override
226        public void onReceive(Context context, Intent intent) {
227            String action = intent.getAction();
228            if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
229                mNitzTimeSetTime = SystemClock.elapsedRealtime();
230            } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
231                mNitzZoneSetTime = SystemClock.elapsedRealtime();
232            }
233        }
234    };
235
236    /** Receiver for ConnectivityManager events */
237    private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
238
239        @Override
240        public void onReceive(Context context, Intent intent) {
241            String action = intent.getAction();
242            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
243                // There is connectivity
244                final ConnectivityManager connManager = (ConnectivityManager) context
245                        .getSystemService(Context.CONNECTIVITY_SERVICE);
246                final NetworkInfo netInfo = connManager.getActiveNetworkInfo();
247                if (netInfo != null) {
248                    // Verify that it's a WIFI connection
249                    if (netInfo.getState() == NetworkInfo.State.CONNECTED &&
250                            (netInfo.getType() == ConnectivityManager.TYPE_WIFI ||
251                                netInfo.getType() == ConnectivityManager.TYPE_ETHERNET) ) {
252                        mHandler.obtainMessage(EVENT_NETWORK_CONNECTED).sendToTarget();
253                    }
254                }
255            }
256        }
257    };
258
259    /** Handler to do the network accesses on */
260    private class MyHandler extends Handler {
261
262        public MyHandler(Looper l) {
263            super(l);
264        }
265
266        @Override
267        public void handleMessage(Message msg) {
268            switch (msg.what) {
269                case EVENT_AUTO_TIME_CHANGED:
270                case EVENT_POLL_NETWORK_TIME:
271                case EVENT_NETWORK_CONNECTED:
272                    onPollNetworkTime(msg.what);
273                    break;
274            }
275        }
276    }
277
278    /** Observer to watch for changes to the AUTO_TIME setting */
279    private static class SettingsObserver extends ContentObserver {
280
281        private int mMsg;
282        private Handler mHandler;
283
284        SettingsObserver(Handler handler, int msg) {
285            super(handler);
286            mHandler = handler;
287            mMsg = msg;
288        }
289
290        void observe(Context context) {
291            ContentResolver resolver = context.getContentResolver();
292            resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
293                    false, this);
294        }
295
296        @Override
297        public void onChange(boolean selfChange) {
298            mHandler.obtainMessage(mMsg).sendToTarget();
299        }
300    }
301}
302