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.content.pm.PackageManager;
27import android.database.ContentObserver;
28import android.net.ConnectivityManager;
29import android.os.Binder;
30import android.os.Handler;
31import android.os.HandlerThread;
32import android.os.Looper;
33import android.os.Message;
34import android.os.SystemClock;
35import android.os.PowerManager;
36import android.provider.Settings;
37import android.util.Log;
38import android.util.NtpTrustedTime;
39import android.util.TimeUtils;
40import android.util.TrustedTime;
41
42import com.android.internal.telephony.TelephonyIntents;
43
44import java.io.FileDescriptor;
45import 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 */
57public 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