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.content.pm.PackageManager; 27 import android.database.ContentObserver; 28 import android.net.ConnectivityManager; 29 import android.os.Binder; 30 import android.os.Handler; 31 import android.os.HandlerThread; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.SystemClock; 35 import android.os.PowerManager; 36 import android.provider.Settings; 37 import android.util.Log; 38 import android.util.NtpTrustedTime; 39 import android.util.TimeUtils; 40 import android.util.TrustedTime; 41 42 import com.android.internal.telephony.TelephonyIntents; 43 import com.android.internal.util.DumpUtils; 44 45 import java.io.FileDescriptor; 46 import 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 */ 58 public 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