blob: a0d305cdf2c6ac1e17a5c0bd036b428fa920a3f9 [file] [log] [blame]
Amith Yamasani6734b9f2010-09-13 16:24:08 -07001/*
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
Amith Yamasani6734b9f2010-09-13 16:24:08 -070019import 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;
Amith Yamasani8d394fa2011-03-01 12:41:04 -080027import android.net.ConnectivityManager;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070028import android.os.Handler;
Amith Yamasani450a16b2013-09-18 16:28:50 -070029import android.os.HandlerThread;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070030import android.os.Looper;
31import android.os.Message;
32import android.os.SystemClock;
33import android.provider.Settings;
34import android.util.Log;
Jeff Sharkey104344e2011-07-10 14:20:41 -070035import android.util.NtpTrustedTime;
36import android.util.TrustedTime;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070037
Jeff Sharkey104344e2011-07-10 14:20:41 -070038import com.android.internal.telephony.TelephonyIntents;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070039
40/**
41 * Monitors the network time and updates the system time if it is out of sync
42 * and there hasn't been any NITZ update from the carrier recently.
43 * If looking up the network time fails for some reason, it tries a few times with a short
44 * interval and then resets to checking on longer intervals.
45 * <p>
46 * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
47 * available.
48 * </p>
49 */
50public class NetworkTimeUpdateService {
51
52 private static final String TAG = "NetworkTimeUpdateService";
53 private static final boolean DBG = false;
54
55 private static final int EVENT_AUTO_TIME_CHANGED = 1;
56 private static final int EVENT_POLL_NETWORK_TIME = 2;
Lorenzo Colittidf590532015-01-22 22:35:33 +090057 private static final int EVENT_NETWORK_CHANGED = 3;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070058
Amith Yamasani6734b9f2010-09-13 16:24:08 -070059 private static final String ACTION_POLL =
60 "com.android.server.NetworkTimeUpdateService.action.POLL";
Amith Yamasani6734b9f2010-09-13 16:24:08 -070061 private static int POLL_REQUEST = 0;
62
63 private static final long NOT_SET = -1;
64 private long mNitzTimeSetTime = NOT_SET;
65 // TODO: Have a way to look up the timezone we are in
66 private long mNitzZoneSetTime = NOT_SET;
67
68 private Context mContext;
Jeff Sharkey104344e2011-07-10 14:20:41 -070069 private TrustedTime mTime;
70
Amith Yamasani6734b9f2010-09-13 16:24:08 -070071 // NTP lookup is done on this thread and handler
72 private Handler mHandler;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070073 private AlarmManager mAlarmManager;
74 private PendingIntent mPendingPollIntent;
75 private SettingsObserver mSettingsObserver;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070076 // The last time that we successfully fetched the NTP time.
77 private long mLastNtpFetchTime = NOT_SET;
Jaewan Kimc3560b82012-11-20 23:58:07 +090078
79 // Normal polling frequency
80 private final long mPollingIntervalMs;
81 // Try-again polling interval, in case the network request failed
82 private final long mPollingIntervalShorterMs;
83 // Number of times to try again
84 private final int mTryAgainTimesMax;
85 // If the time difference is greater than this threshold, then update the time.
86 private final int mTimeErrorThresholdMs;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070087 // Keeps track of how many quick attempts were made to fetch NTP time.
88 // During bootup, the network may not have been up yet, or it's taking time for the
89 // connection to happen.
90 private int mTryAgainCounter;
91
92 public NetworkTimeUpdateService(Context context) {
93 mContext = context;
Jeff Sharkey104344e2011-07-10 14:20:41 -070094 mTime = NtpTrustedTime.getInstance(context);
Amith Yamasani6734b9f2010-09-13 16:24:08 -070095 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
96 Intent pollIntent = new Intent(ACTION_POLL, null);
97 mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
Jaewan Kimc3560b82012-11-20 23:58:07 +090098
99 mPollingIntervalMs = mContext.getResources().getInteger(
100 com.android.internal.R.integer.config_ntpPollingInterval);
101 mPollingIntervalShorterMs = mContext.getResources().getInteger(
102 com.android.internal.R.integer.config_ntpPollingIntervalShorter);
103 mTryAgainTimesMax = mContext.getResources().getInteger(
104 com.android.internal.R.integer.config_ntpRetry);
105 mTimeErrorThresholdMs = mContext.getResources().getInteger(
106 com.android.internal.R.integer.config_ntpThreshold);
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700107 }
108
109 /** Initialize the receivers and initiate the first NTP request */
Svetoslav Ganova0027152013-06-25 14:59:53 -0700110 public void systemRunning() {
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700111 registerForTelephonyIntents();
112 registerForAlarms();
Amith Yamasani8d394fa2011-03-01 12:41:04 -0800113 registerForConnectivityIntents();
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700114
Amith Yamasani450a16b2013-09-18 16:28:50 -0700115 HandlerThread thread = new HandlerThread(TAG);
116 thread.start();
117 mHandler = new MyHandler(thread.getLooper());
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700118 // Check the network time on the new thread
119 mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
120
121 mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
122 mSettingsObserver.observe(mContext);
123 }
124
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700125 private void registerForTelephonyIntents() {
126 IntentFilter intentFilter = new IntentFilter();
127 intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
128 intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
129 mContext.registerReceiver(mNitzReceiver, intentFilter);
130 }
131
132 private void registerForAlarms() {
133 mContext.registerReceiver(
134 new BroadcastReceiver() {
135 @Override
136 public void onReceive(Context context, Intent intent) {
137 mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
138 }
139 }, new IntentFilter(ACTION_POLL));
140 }
141
Amith Yamasani8d394fa2011-03-01 12:41:04 -0800142 private void registerForConnectivityIntents() {
143 IntentFilter intentFilter = new IntentFilter();
144 intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
145 mContext.registerReceiver(mConnectivityReceiver, intentFilter);
146 }
147
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700148 private void onPollNetworkTime(int event) {
149 // If Automatic time is not set, don't bother.
150 if (!isAutomaticTimeRequested()) return;
151
152 final long refTime = SystemClock.elapsedRealtime();
Jaewan Kimc3560b82012-11-20 23:58:07 +0900153 // If NITZ time was received less than mPollingIntervalMs time ago,
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700154 // no need to sync to NTP.
Jaewan Kimc3560b82012-11-20 23:58:07 +0900155 if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
156 resetAlarm(mPollingIntervalMs);
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700157 return;
158 }
159 final long currentTime = System.currentTimeMillis();
160 if (DBG) Log.d(TAG, "System time = " + currentTime);
161 // Get the NTP time
Jaewan Kimc3560b82012-11-20 23:58:07 +0900162 if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700163 || event == EVENT_AUTO_TIME_CHANGED) {
164 if (DBG) Log.d(TAG, "Before Ntp fetch");
Jeff Sharkey104344e2011-07-10 14:20:41 -0700165
166 // force refresh NTP cache when outdated
Jaewan Kimc3560b82012-11-20 23:58:07 +0900167 if (mTime.getCacheAge() >= mPollingIntervalMs) {
Jeff Sharkey104344e2011-07-10 14:20:41 -0700168 mTime.forceRefresh();
169 }
170
171 // only update when NTP time is fresh
Jaewan Kimc3560b82012-11-20 23:58:07 +0900172 if (mTime.getCacheAge() < mPollingIntervalMs) {
Jeff Sharkey104344e2011-07-10 14:20:41 -0700173 final long ntp = mTime.currentTimeMillis();
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700174 mTryAgainCounter = 0;
Amith Yamasani708d5d42012-05-02 11:50:31 -0700175 // If the clock is more than N seconds off or this is the first time it's been
176 // fetched since boot, set the current time.
Jaewan Kimc3560b82012-11-20 23:58:07 +0900177 if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
Amith Yamasani708d5d42012-05-02 11:50:31 -0700178 || mLastNtpFetchTime == NOT_SET) {
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700179 // Set the system time
Amith Yamasani708d5d42012-05-02 11:50:31 -0700180 if (DBG && mLastNtpFetchTime == NOT_SET
Jaewan Kimc3560b82012-11-20 23:58:07 +0900181 && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
Amith Yamasani708d5d42012-05-02 11:50:31 -0700182 Log.d(TAG, "For initial setup, rtc = " + currentTime);
183 }
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700184 if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
185 // Make sure we don't overflow, since it's going to be converted to an int
186 if (ntp / 1000 < Integer.MAX_VALUE) {
187 SystemClock.setCurrentTimeMillis(ntp);
188 }
189 } else {
190 if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
191 }
Amith Yamasani708d5d42012-05-02 11:50:31 -0700192 mLastNtpFetchTime = SystemClock.elapsedRealtime();
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700193 } else {
194 // Try again shortly
195 mTryAgainCounter++;
Jaewan Kimc3560b82012-11-20 23:58:07 +0900196 if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
197 resetAlarm(mPollingIntervalShorterMs);
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700198 } else {
199 // Try much later
200 mTryAgainCounter = 0;
Jaewan Kimc3560b82012-11-20 23:58:07 +0900201 resetAlarm(mPollingIntervalMs);
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700202 }
203 return;
204 }
205 }
Jaewan Kimc3560b82012-11-20 23:58:07 +0900206 resetAlarm(mPollingIntervalMs);
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700207 }
208
209 /**
210 * Cancel old alarm and starts a new one for the specified interval.
211 *
212 * @param interval when to trigger the alarm, starting from now.
213 */
214 private void resetAlarm(long interval) {
215 mAlarmManager.cancel(mPendingPollIntent);
216 long now = SystemClock.elapsedRealtime();
217 long next = now + interval;
218 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
219 }
220
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700221 /**
222 * Checks if the user prefers to automatically set the time.
223 */
224 private boolean isAutomaticTimeRequested() {
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700225 return Settings.Global.getInt(
226 mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700227 }
228
229 /** Receiver for Nitz time events */
230 private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
231
232 @Override
233 public void onReceive(Context context, Intent intent) {
234 String action = intent.getAction();
235 if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
236 mNitzTimeSetTime = SystemClock.elapsedRealtime();
237 } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
238 mNitzZoneSetTime = SystemClock.elapsedRealtime();
239 }
240 }
241 };
242
Amith Yamasani8d394fa2011-03-01 12:41:04 -0800243 /** Receiver for ConnectivityManager events */
244 private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
245
246 @Override
247 public void onReceive(Context context, Intent intent) {
248 String action = intent.getAction();
249 if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
Lorenzo Colittidf590532015-01-22 22:35:33 +0900250 // Don't bother checking if we have connectivity, NtpTrustedTime does that for us.
251 mHandler.obtainMessage(EVENT_NETWORK_CHANGED).sendToTarget();
Amith Yamasani8d394fa2011-03-01 12:41:04 -0800252 }
253 }
254 };
255
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700256 /** 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:
Lorenzo Colittidf590532015-01-22 22:35:33 +0900268 case EVENT_NETWORK_CHANGED:
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700269 onPollNetworkTime(msg.what);
270 break;
271 }
272 }
273 }
274
275 /** Observer to watch for changes to the AUTO_TIME setting */
276 private static class SettingsObserver extends ContentObserver {
277
278 private int mMsg;
279 private Handler mHandler;
280
281 SettingsObserver(Handler handler, int msg) {
282 super(handler);
283 mHandler = handler;
284 mMsg = msg;
285 }
286
287 void observe(Context context) {
288 ContentResolver resolver = context.getContentResolver();
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700289 resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700290 false, this);
291 }
292
293 @Override
294 public void onChange(boolean selfChange) {
295 mHandler.obtainMessage(mMsg).sendToTarget();
296 }
297 }
298}