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 17 package com.android.server; 18 19 import android.app.AlarmManager; 20 import android.app.PendingIntent; 21 import android.content.BroadcastReceiver; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.database.ContentObserver; 27 import android.net.ConnectivityManager; 28 import android.net.NetworkInfo; 29 import android.os.Handler; 30 import android.os.HandlerThread; 31 import android.os.Looper; 32 import android.os.Message; 33 import android.os.SystemClock; 34 import android.provider.Settings; 35 import android.util.Log; 36 import android.util.NtpTrustedTime; 37 import android.util.TrustedTime; 38 39 import 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 */ 51 public 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 NetworkTimeUpdateService(Context context)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 */ systemReady()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 registerForTelephonyIntents()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 registerForAlarms()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 registerForConnectivityIntents()135 private void registerForConnectivityIntents() { 136 IntentFilter intentFilter = new IntentFilter(); 137 intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); 138 mContext.registerReceiver(mConnectivityReceiver, intentFilter); 139 } 140 onPollNetworkTime(int event)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 */ resetAlarm(long interval)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 */ isAutomaticTimeRequested()217 private boolean isAutomaticTimeRequested() { 218 return Settings.System.getInt(mContext.getContentResolver(), Settings.System.AUTO_TIME, 0) 219 != 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 MyHandler(Looper l)262 public MyHandler(Looper l) { 263 super(l); 264 } 265 266 @Override handleMessage(Message msg)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 SettingsObserver(Handler handler, int msg)284 SettingsObserver(Handler handler, int msg) { 285 super(handler); 286 mHandler = handler; 287 mMsg = msg; 288 } 289 observe(Context context)290 void observe(Context context) { 291 ContentResolver resolver = context.getContentResolver(); 292 resolver.registerContentObserver(Settings.System.getUriFor(Settings.System.AUTO_TIME), 293 false, this); 294 } 295 296 @Override onChange(boolean selfChange)297 public void onChange(boolean selfChange) { 298 mHandler.obtainMessage(mMsg).sendToTarget(); 299 } 300 } 301 } 302