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 private static final String ACTION_POLL = 61 "com.android.server.NetworkTimeUpdateService.action.POLL"; 62 private static int POLL_REQUEST = 0; 63 64 private static final long NOT_SET = -1; 65 private long mNitzTimeSetTime = NOT_SET; 66 // TODO: Have a way to look up the timezone we are in 67 private long mNitzZoneSetTime = NOT_SET; 68 69 private Context mContext; 70 private TrustedTime mTime; 71 72 // NTP lookup is done on this thread and handler 73 private Handler mHandler; 74 private AlarmManager mAlarmManager; 75 private PendingIntent mPendingPollIntent; 76 private SettingsObserver mSettingsObserver; 77 // The last time that we successfully fetched the NTP time. 78 private long mLastNtpFetchTime = NOT_SET; 79 80 // Normal polling frequency 81 private final long mPollingIntervalMs; 82 // Try-again polling interval, in case the network request failed 83 private final long mPollingIntervalShorterMs; 84 // Number of times to try again 85 private final int mTryAgainTimesMax; 86 // If the time difference is greater than this threshold, then update the time. 87 private final int mTimeErrorThresholdMs; 88 // Keeps track of how many quick attempts were made to fetch NTP time. 89 // During bootup, the network may not have been up yet, or it's taking time for the 90 // connection to happen. 91 private int mTryAgainCounter; 92 93 public NetworkTimeUpdateService(Context context) { 94 mContext = context; 95 mTime = NtpTrustedTime.getInstance(context); 96 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 97 Intent pollIntent = new Intent(ACTION_POLL, null); 98 mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0); 99 100 mPollingIntervalMs = mContext.getResources().getInteger( 101 com.android.internal.R.integer.config_ntpPollingInterval); 102 mPollingIntervalShorterMs = mContext.getResources().getInteger( 103 com.android.internal.R.integer.config_ntpPollingIntervalShorter); 104 mTryAgainTimesMax = mContext.getResources().getInteger( 105 com.android.internal.R.integer.config_ntpRetry); 106 mTimeErrorThresholdMs = mContext.getResources().getInteger( 107 com.android.internal.R.integer.config_ntpThreshold); 108 } 109 110 /** Initialize the receivers and initiate the first NTP request */ 111 public void systemRunning() { 112 registerForTelephonyIntents(); 113 registerForAlarms(); 114 registerForConnectivityIntents(); 115 116 HandlerThread thread = new HandlerThread(TAG); 117 thread.start(); 118 mHandler = new MyHandler(thread.getLooper()); 119 // Check the network time on the new thread 120 mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget(); 121 122 mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED); 123 mSettingsObserver.observe(mContext); 124 } 125 126 private void registerForTelephonyIntents() { 127 IntentFilter intentFilter = new IntentFilter(); 128 intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME); 129 intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE); 130 mContext.registerReceiver(mNitzReceiver, intentFilter); 131 } 132 133 private void registerForAlarms() { 134 mContext.registerReceiver( 135 new BroadcastReceiver() { 136 @Override 137 public void onReceive(Context context, Intent intent) { 138 mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget(); 139 } 140 }, new IntentFilter(ACTION_POLL)); 141 } 142 143 private void registerForConnectivityIntents() { 144 IntentFilter intentFilter = new IntentFilter(); 145 intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); 146 mContext.registerReceiver(mConnectivityReceiver, intentFilter); 147 } 148 149 private void onPollNetworkTime(int event) { 150 // If Automatic time is not set, don't bother. 151 if (!isAutomaticTimeRequested()) return; 152 153 final long refTime = SystemClock.elapsedRealtime(); 154 // If NITZ time was received less than mPollingIntervalMs time ago, 155 // no need to sync to NTP. 156 if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) { 157 resetAlarm(mPollingIntervalMs); 158 return; 159 } 160 final long currentTime = System.currentTimeMillis(); 161 if (DBG) Log.d(TAG, "System time = " + currentTime); 162 // Get the NTP time 163 if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs 164 || event == EVENT_AUTO_TIME_CHANGED) { 165 if (DBG) Log.d(TAG, "Before Ntp fetch"); 166 167 // force refresh NTP cache when outdated 168 if (mTime.getCacheAge() >= mPollingIntervalMs) { 169 mTime.forceRefresh(); 170 } 171 172 // only update when NTP time is fresh 173 if (mTime.getCacheAge() < mPollingIntervalMs) { 174 final long ntp = mTime.currentTimeMillis(); 175 mTryAgainCounter = 0; 176 // If the clock is more than N seconds off or this is the first time it's been 177 // fetched since boot, set the current time. 178 if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs 179 || mLastNtpFetchTime == NOT_SET) { 180 // Set the system time 181 if (DBG && mLastNtpFetchTime == NOT_SET 182 && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) { 183 Log.d(TAG, "For initial setup, rtc = " + currentTime); 184 } 185 if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp); 186 // Make sure we don't overflow, since it's going to be converted to an int 187 if (ntp / 1000 < Integer.MAX_VALUE) { 188 SystemClock.setCurrentTimeMillis(ntp); 189 } 190 } else { 191 if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp); 192 } 193 mLastNtpFetchTime = SystemClock.elapsedRealtime(); 194 } else { 195 // Try again shortly 196 mTryAgainCounter++; 197 if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) { 198 resetAlarm(mPollingIntervalShorterMs); 199 } else { 200 // Try much later 201 mTryAgainCounter = 0; 202 resetAlarm(mPollingIntervalMs); 203 } 204 return; 205 } 206 } 207 resetAlarm(mPollingIntervalMs); 208 } 209 210 /** 211 * Cancel old alarm and starts a new one for the specified interval. 212 * 213 * @param interval when to trigger the alarm, starting from now. 214 */ 215 private void resetAlarm(long interval) { 216 mAlarmManager.cancel(mPendingPollIntent); 217 long now = SystemClock.elapsedRealtime(); 218 long next = now + interval; 219 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent); 220 } 221 222 /** 223 * Checks if the user prefers to automatically set the time. 224 */ 225 private boolean isAutomaticTimeRequested() { 226 return Settings.Global.getInt( 227 mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0; 228 } 229 230 /** Receiver for Nitz time events */ 231 private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() { 232 233 @Override 234 public void onReceive(Context context, Intent intent) { 235 String action = intent.getAction(); 236 if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) { 237 mNitzTimeSetTime = SystemClock.elapsedRealtime(); 238 } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) { 239 mNitzZoneSetTime = SystemClock.elapsedRealtime(); 240 } 241 } 242 }; 243 244 /** Receiver for ConnectivityManager events */ 245 private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() { 246 247 @Override 248 public void onReceive(Context context, Intent intent) { 249 String action = intent.getAction(); 250 if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) { 251 // There is connectivity 252 final ConnectivityManager connManager = (ConnectivityManager) context 253 .getSystemService(Context.CONNECTIVITY_SERVICE); 254 final NetworkInfo netInfo = connManager.getActiveNetworkInfo(); 255 if (netInfo != null) { 256 // Verify that it's a WIFI connection 257 if (netInfo.getState() == NetworkInfo.State.CONNECTED && 258 (netInfo.getType() == ConnectivityManager.TYPE_WIFI || 259 netInfo.getType() == ConnectivityManager.TYPE_ETHERNET) ) { 260 mHandler.obtainMessage(EVENT_NETWORK_CONNECTED).sendToTarget(); 261 } 262 } 263 } 264 } 265 }; 266 267 /** Handler to do the network accesses on */ 268 private class MyHandler extends Handler { 269 270 public MyHandler(Looper l) { 271 super(l); 272 } 273 274 @Override 275 public void handleMessage(Message msg) { 276 switch (msg.what) { 277 case EVENT_AUTO_TIME_CHANGED: 278 case EVENT_POLL_NETWORK_TIME: 279 case EVENT_NETWORK_CONNECTED: 280 onPollNetworkTime(msg.what); 281 break; 282 } 283 } 284 } 285 286 /** Observer to watch for changes to the AUTO_TIME setting */ 287 private static class SettingsObserver extends ContentObserver { 288 289 private int mMsg; 290 private Handler mHandler; 291 292 SettingsObserver(Handler handler, int msg) { 293 super(handler); 294 mHandler = handler; 295 mMsg = msg; 296 } 297 298 void observe(Context context) { 299 ContentResolver resolver = context.getContentResolver(); 300 resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME), 301 false, this); 302 } 303 304 @Override 305 public void onChange(boolean selfChange) { 306 mHandler.obtainMessage(mMsg).sendToTarget(); 307 } 308 } 309 } 310