blob: f7fe39ecfd680e38341e73ea2c8828da5a6d6ed1 [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;
28import android.net.NetworkInfo;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070029import 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;
Jeff Sharkey104344e2011-07-10 14:20:41 -070036import android.util.NtpTrustedTime;
37import android.util.TrustedTime;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070038
Jeff Sharkey104344e2011-07-10 14:20:41 -070039import com.android.internal.telephony.TelephonyIntents;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070040
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;
Amith Yamasani8d394fa2011-03-01 12:41:04 -080058 private static final int EVENT_WIFI_CONNECTED = 3;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070059
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;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070066 /** 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";
Amith Yamasani6734b9f2010-09-13 16:24:08 -070071 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;
Jeff Sharkey104344e2011-07-10 14:20:41 -070079 private TrustedTime mTime;
80
Amith Yamasani6734b9f2010-09-13 16:24:08 -070081 // 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;
Amith Yamasani6734b9f2010-09-13 16:24:08 -070087 // 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;
Jeff Sharkey104344e2011-07-10 14:20:41 -070096 mTime = NtpTrustedTime.getInstance(context);
Amith Yamasani6734b9f2010-09-13 16:24:08 -070097 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() {
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700104 registerForTelephonyIntents();
105 registerForAlarms();
Amith Yamasani8d394fa2011-03-01 12:41:04 -0800106 registerForConnectivityIntents();
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700107
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
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700118 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
Amith Yamasani8d394fa2011-03-01 12:41:04 -0800135 private void registerForConnectivityIntents() {
136 IntentFilter intentFilter = new IntentFilter();
137 intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
138 mContext.registerReceiver(mConnectivityReceiver, intentFilter);
139 }
140
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700141 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");
Jeff Sharkey104344e2011-07-10 14:20:41 -0700158
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();
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700167 mTryAgainCounter = 0;
168 mLastNtpFetchTime = SystemClock.elapsedRealtime();
169 if (Math.abs(ntp - currentTime) > TIME_ERROR_THRESHOLD_MS) {
170 // Set the system time
171 if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
172 // Make sure we don't overflow, since it's going to be converted to an int
173 if (ntp / 1000 < Integer.MAX_VALUE) {
174 SystemClock.setCurrentTimeMillis(ntp);
175 }
176 } else {
177 if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
178 }
179 } else {
180 // Try again shortly
181 mTryAgainCounter++;
182 if (mTryAgainCounter <= TRY_AGAIN_TIMES_MAX) {
183 resetAlarm(POLLING_INTERVAL_SHORTER_MS);
184 } else {
185 // Try much later
186 mTryAgainCounter = 0;
187 resetAlarm(POLLING_INTERVAL_MS);
188 }
189 return;
190 }
191 }
192 resetAlarm(POLLING_INTERVAL_MS);
193 }
194
195 /**
196 * Cancel old alarm and starts a new one for the specified interval.
197 *
198 * @param interval when to trigger the alarm, starting from now.
199 */
200 private void resetAlarm(long interval) {
201 mAlarmManager.cancel(mPendingPollIntent);
202 long now = SystemClock.elapsedRealtime();
203 long next = now + interval;
204 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
205 }
206
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700207 /**
208 * Checks if the user prefers to automatically set the time.
209 */
210 private boolean isAutomaticTimeRequested() {
211 return Settings.System.getInt(mContext.getContentResolver(), Settings.System.AUTO_TIME, 0)
212 != 0;
213 }
214
215 /** Receiver for Nitz time events */
216 private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
217
218 @Override
219 public void onReceive(Context context, Intent intent) {
220 String action = intent.getAction();
221 if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
222 mNitzTimeSetTime = SystemClock.elapsedRealtime();
223 } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
224 mNitzZoneSetTime = SystemClock.elapsedRealtime();
225 }
226 }
227 };
228
Amith Yamasani8d394fa2011-03-01 12:41:04 -0800229 /** Receiver for ConnectivityManager events */
230 private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
231
232 @Override
233 public void onReceive(Context context, Intent intent) {
234 String action = intent.getAction();
235 if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
236 // There is connectivity
237 NetworkInfo netInfo = (NetworkInfo)intent.getParcelableExtra(
238 ConnectivityManager.EXTRA_NETWORK_INFO);
239 if (netInfo != null) {
240 // Verify that it's a WIFI connection
241 if (netInfo.getState() == NetworkInfo.State.CONNECTED &&
242 netInfo.getType() == ConnectivityManager.TYPE_WIFI ) {
243 mHandler.obtainMessage(EVENT_WIFI_CONNECTED).sendToTarget();
244 }
245 }
246 }
247 }
248 };
249
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700250 /** Handler to do the network accesses on */
251 private class MyHandler extends Handler {
252
253 public MyHandler(Looper l) {
254 super(l);
255 }
256
257 @Override
258 public void handleMessage(Message msg) {
259 switch (msg.what) {
260 case EVENT_AUTO_TIME_CHANGED:
261 case EVENT_POLL_NETWORK_TIME:
Amith Yamasani8d394fa2011-03-01 12:41:04 -0800262 case EVENT_WIFI_CONNECTED:
Amith Yamasani6734b9f2010-09-13 16:24:08 -0700263 onPollNetworkTime(msg.what);
264 break;
265 }
266 }
267 }
268
269 /** Observer to watch for changes to the AUTO_TIME setting */
270 private static class SettingsObserver extends ContentObserver {
271
272 private int mMsg;
273 private Handler mHandler;
274
275 SettingsObserver(Handler handler, int msg) {
276 super(handler);
277 mHandler = handler;
278 mMsg = msg;
279 }
280
281 void observe(Context context) {
282 ContentResolver resolver = context.getContentResolver();
283 resolver.registerContentObserver(Settings.System.getUriFor(Settings.System.AUTO_TIME),
284 false, this);
285 }
286
287 @Override
288 public void onChange(boolean selfChange) {
289 mHandler.obtainMessage(mMsg).sendToTarget();
290 }
291 }
292}