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;
43import com.android.internal.util.DumpUtils;
44
45import java.io.FileDescriptor;
46import 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 */
58public 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