diff --git a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java index b96af89fad299..41824de5953c8 100644 --- a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java +++ b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java @@ -18,6 +18,7 @@ package com.android.server.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.time.UnixEpochTime; @@ -33,8 +34,6 @@ import android.net.Network; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; import android.os.PowerManager; import android.os.ResultReceiver; import android.os.ShellCallback; @@ -45,6 +44,7 @@ import android.util.Log; import android.util.NtpTrustedTime; import android.util.NtpTrustedTime.TimeResult; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.DumpUtils; import com.android.server.LocalServices; @@ -66,17 +66,13 @@ public class NetworkTimeUpdateService extends Binder { private static final String TAG = "NetworkTimeUpdateService"; private static final boolean DBG = false; - private static final int EVENT_AUTO_TIME_ENABLED = 1; - private static final int EVENT_POLL_NETWORK_TIME = 2; - private static final int EVENT_NETWORK_CHANGED = 3; - private static final String ACTION_POLL = "com.android.server.timedetector.NetworkTimeUpdateService.action.POLL"; - private static final int POLL_REQUEST = 0; + private final Object mLock = new Object(); private final Context mContext; - private final NtpTrustedTime mTime; + private final NtpTrustedTime mNtpTrustedTime; private final AlarmManager mAlarmManager; private final TimeDetectorInternal mTimeDetectorInternal; private final ConnectivityManager mCM; @@ -84,9 +80,9 @@ public class NetworkTimeUpdateService extends Binder { private final PowerManager.WakeLock mWakeLock; // Normal polling frequency - private final long mPollingIntervalMs; + private final int mNormalPollingIntervalMillis; // Try-again polling interval, in case the network request failed - private final long mPollingIntervalShorterMs; + private final int mShortPollingIntervalMillis; // Number of times to try again private final int mTryAgainTimesMax; @@ -94,18 +90,13 @@ public class NetworkTimeUpdateService extends Binder { * A log that records the decisions to fetch a network time update. * This is logged in bug reports to assist with debugging issues with network time suggestions. */ - @NonNull private final LocalLog mLocalLog = new LocalLog(30, false /* useLocalTimestamps */); - // NTP lookup is done on this thread and handler - // @NonNull after systemRunning() - private Handler mHandler; - // @NonNull after systemRunning() - private AutoTimeSettingObserver mAutoTimeSettingObserver; - // @NonNull after systemRunning() - private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback; + // Blocking NTP lookup is done using this handler + private final Handler mHandler; // This field is only updated and accessed by the mHandler thread (except dump()). + @GuardedBy("mLock") @Nullable private Network mDefaultNetwork = null; @@ -113,54 +104,76 @@ public class NetworkTimeUpdateService extends Binder { // During bootup, the network may not have been up yet, or it's taking time for the // connection to happen. // This field is only updated and accessed by the mHandler thread (except dump()). + @GuardedBy("mLock") private int mTryAgainCounter; public NetworkTimeUpdateService(@NonNull Context context) { mContext = Objects.requireNonNull(context); - mTime = NtpTrustedTime.getInstance(context); mAlarmManager = mContext.getSystemService(AlarmManager.class); mTimeDetectorInternal = LocalServices.getService(TimeDetectorInternal.class); mCM = mContext.getSystemService(ConnectivityManager.class); - - Intent pollIntent = new Intent(ACTION_POLL, null); - // Broadcast alarms sent by system are immutable - mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, - PendingIntent.FLAG_IMMUTABLE); - - mPollingIntervalMs = mContext.getResources().getInteger( - com.android.internal.R.integer.config_ntpPollingInterval); - mPollingIntervalShorterMs = mContext.getResources().getInteger( - com.android.internal.R.integer.config_ntpPollingIntervalShorter); - mTryAgainTimesMax = mContext.getResources().getInteger( - com.android.internal.R.integer.config_ntpRetry); - mWakeLock = context.getSystemService(PowerManager.class).newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, TAG); + mNtpTrustedTime = NtpTrustedTime.getInstance(context); + + mTryAgainTimesMax = mContext.getResources().getInteger( + com.android.internal.R.integer.config_ntpRetry); + mNormalPollingIntervalMillis = mContext.getResources().getInteger( + com.android.internal.R.integer.config_ntpPollingInterval); + mShortPollingIntervalMillis = mContext.getResources().getInteger( + com.android.internal.R.integer.config_ntpPollingIntervalShorter); + + // Broadcast alarms sent by system are immutable + Intent pollIntent = new Intent(ACTION_POLL, null); + mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, + pollIntent, PendingIntent.FLAG_IMMUTABLE); + + HandlerThread thread = new HandlerThread(TAG); + thread.start(); + mHandler = thread.getThreadHandler(); } /** Initialize the receivers and initiate the first NTP request */ public void systemRunning() { - registerForAlarms(); - - HandlerThread thread = new HandlerThread(TAG); - thread.start(); - mHandler = new MyHandler(thread.getLooper()); - mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback(); - mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler); - - mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler, - EVENT_AUTO_TIME_ENABLED); - mAutoTimeSettingObserver.observe(); - } - - private void registerForAlarms() { + // Listen for scheduled refreshes. mContext.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget(); + onPollNetworkTime("scheduled refresh"); } - }, new IntentFilter(ACTION_POLL)); + }, + new IntentFilter(ACTION_POLL), + /*broadcastPermission=*/ null, + mHandler); + + // Listen for network connectivity changes. + NetworkTimeUpdateCallback networkTimeUpdateCallback = new NetworkTimeUpdateCallback(); + mCM.registerDefaultNetworkCallback(networkTimeUpdateCallback, mHandler); + + // Listen for user settings changes. + ContentResolver resolver = mContext.getContentResolver(); + AutoTimeSettingObserver autoTimeSettingObserver = + new AutoTimeSettingObserver(mHandler, mContext); + resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME), + false, autoTimeSettingObserver); + } + + /** + * Overrides the NTP server config for tests. Passing {@code null} to a parameter clears the + * test value, i.e. so the normal value will be used next time. + */ + @RequiresPermission(android.Manifest.permission.SET_TIME) + void setServerConfigForTests(@Nullable NtpTrustedTime.NtpConfig ntpConfig) { + mContext.enforceCallingPermission( + android.Manifest.permission.SET_TIME, "set NTP server config for tests"); + + final long token = Binder.clearCallingIdentity(); + try { + mNtpTrustedTime.setServerConfigForTests(ntpConfig); + } finally { + Binder.restoreCallingIdentity(token); + } } /** @@ -168,15 +181,14 @@ public class NetworkTimeUpdateService extends Binder { * *

This operation takes place in the calling thread rather than the service's handler thread. */ + @RequiresPermission(android.Manifest.permission.SET_TIME) void clearTimeForTests() { mContext.enforceCallingPermission( android.Manifest.permission.SET_TIME, "clear latest network time"); final long token = Binder.clearCallingIdentity(); try { - mTime.clearCachedTimeResult(); - - mLocalLog.log("clearTimeForTests"); + mNtpTrustedTime.clearCachedTimeResult(); } finally { Binder.restoreCallingIdentity(token); } @@ -186,20 +198,28 @@ public class NetworkTimeUpdateService extends Binder { * Forces the service to refresh the NTP time. * *

This operation takes place in the calling thread rather than the service's handler thread. - * This method does not affect currently scheduled refreshes. If the NTP request is successful - * it will make an (asynchronously handled) suggestion to the time detector. + * This method does not affect currently scheduled refreshes. + * + *

If the NTP request is successful it will synchronously make a suggestion to the time + * detector, which will be asynchronously handled; therefore the effects are not guaranteed to + * be visible when this call returns. */ + @RequiresPermission(android.Manifest.permission.SET_TIME) boolean forceRefreshForTests() { mContext.enforceCallingPermission( android.Manifest.permission.SET_TIME, "force network time refresh"); final long token = Binder.clearCallingIdentity(); try { - boolean success = mTime.forceRefresh(); - mLocalLog.log("forceRefreshForTests: success=" + success); + Network network; + synchronized (mLock) { + network = mDefaultNetwork; + } + if (network == null) return false; + boolean success = mNtpTrustedTime.forceRefresh(network); if (success) { - makeNetworkTimeSuggestion(mTime.getCachedTimeResult(), + makeNetworkTimeSuggestion(mNtpTrustedTime.getCachedTimeResult(), "Origin: NetworkTimeUpdateService: forceRefreshForTests"); } return success; @@ -208,49 +228,41 @@ public class NetworkTimeUpdateService extends Binder { } } - /** - * Overrides the NTP server config for tests. Passing {@code null} to a parameter clears the - * test value, i.e. so the normal value will be used next time. - */ - void setServerConfigForTests(@Nullable NtpTrustedTime.NtpConfig ntpConfig) { - mContext.enforceCallingPermission( - android.Manifest.permission.SET_TIME, "set NTP server config for tests"); - - final long token = Binder.clearCallingIdentity(); - try { - mLocalLog.log("Setting server config for tests: ntpConnectionInfo=" + ntpConfig); - mTime.setServerConfigForTests(ntpConfig); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - private void onPollNetworkTime(int event) { + private void onPollNetworkTime(@NonNull String reason) { // If we don't have any default network, don't bother. - Network network = mDefaultNetwork; + Network network; + synchronized (mLock) { + network = mDefaultNetwork; + } if (network == null) return; mWakeLock.acquire(); try { - onPollNetworkTimeUnderWakeLock(network, event); + onPollNetworkTimeUnderWakeLock(network, reason); } finally { mWakeLock.release(); } } - private void onPollNetworkTimeUnderWakeLock(@NonNull Network network, int event) { + private void onPollNetworkTimeUnderWakeLock( + @NonNull Network network, @NonNull String reason) { long currentElapsedRealtimeMillis = SystemClock.elapsedRealtime(); + final int maxNetworkTimeAgeMillis = mNormalPollingIntervalMillis; // Force an NTP fix when outdated - NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult(); - if (cachedNtpResult == null || cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis) - >= mPollingIntervalMs) { + NtpTrustedTime.TimeResult cachedNtpResult = mNtpTrustedTime.getCachedTimeResult(); + if (cachedNtpResult == null + || cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis) + >= maxNetworkTimeAgeMillis) { if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh using network=" + network); - boolean isSuccessful = mTime.forceRefresh(network); - if (isSuccessful) { - mTryAgainCounter = 0; + boolean success = mNtpTrustedTime.forceRefresh(network); + if (success) { + synchronized (mLock) { + mTryAgainCounter = 0; + } } else { - String logMsg = "forceRefresh() returned false: cachedNtpResult=" + cachedNtpResult + String logMsg = "forceRefresh() returned false:" + + " cachedNtpResult=" + cachedNtpResult + ", currentElapsedRealtimeMillis=" + currentElapsedRealtimeMillis; if (DBG) { @@ -259,32 +271,35 @@ public class NetworkTimeUpdateService extends Binder { mLocalLog.log(logMsg); } - cachedNtpResult = mTime.getCachedTimeResult(); + cachedNtpResult = mNtpTrustedTime.getCachedTimeResult(); } if (cachedNtpResult != null && cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis) - < mPollingIntervalMs) { + < maxNetworkTimeAgeMillis) { // Obtained fresh fix; schedule next normal update - resetAlarm(mPollingIntervalMs + scheduleNextRefresh(mNormalPollingIntervalMillis - cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis)); - makeNetworkTimeSuggestion(cachedNtpResult, - "Origin: NetworkTimeUpdateService. event=" + event); + makeNetworkTimeSuggestion(cachedNtpResult, reason); } else { - // No fresh fix; schedule retry - mTryAgainCounter++; - if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) { - resetAlarm(mPollingIntervalShorterMs); - } else { - // Try much later - String logMsg = "mTryAgainTimesMax exceeded, cachedNtpResult=" + cachedNtpResult; - if (DBG) { - Log.d(TAG, logMsg); + synchronized (mLock) { + // No fresh fix; schedule retry + mTryAgainCounter++; + if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) { + scheduleNextRefresh(mShortPollingIntervalMillis); + } else { + // Try much later + String logMsg = "mTryAgainTimesMax exceeded," + + " cachedNtpResult=" + cachedNtpResult; + if (DBG) { + Log.d(TAG, logMsg); + } + mLocalLog.log(logMsg); + mTryAgainCounter = 0; + + scheduleNextRefresh(mNormalPollingIntervalMillis); } - mLocalLog.log(logMsg); - mTryAgainCounter = 0; - resetAlarm(mPollingIntervalMs); } } } @@ -304,46 +319,35 @@ public class NetworkTimeUpdateService extends Binder { /** * Cancel old alarm and starts a new one for the specified interval. * - * @param interval when to trigger the alarm, starting from now. + * @param delayMillis when to trigger the alarm, starting from now. */ - private void resetAlarm(long interval) { + private void scheduleNextRefresh(long delayMillis) { mAlarmManager.cancel(mPendingPollIntent); long now = SystemClock.elapsedRealtime(); - long next = now + interval; + long next = now + delayMillis; mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent); } - /** Handler to do the network accesses on */ - private class MyHandler extends Handler { - - MyHandler(Looper l) { - super(l); - } - - @Override - public void handleMessage(@NonNull Message msg) { - switch (msg.what) { - case EVENT_AUTO_TIME_ENABLED: - case EVENT_POLL_NETWORK_TIME: - case EVENT_NETWORK_CHANGED: - onPollNetworkTime(msg.what); - break; - } - } - } - + // All callbacks will be invoked using mHandler because of how the callback is registered. private class NetworkTimeUpdateCallback extends NetworkCallback { @Override public void onAvailable(@NonNull Network network) { Log.d(TAG, String.format("New default network %s; checking time.", network)); - mDefaultNetwork = network; + synchronized (mLock) { + mDefaultNetwork = network; + } + // Running on mHandler so invoke directly. - onPollNetworkTime(EVENT_NETWORK_CHANGED); + onPollNetworkTime("network available"); } @Override public void onLost(@NonNull Network network) { - if (network.equals(mDefaultNetwork)) mDefaultNetwork = null; + synchronized (mLock) { + if (network.equals(mDefaultNetwork)) { + mDefaultNetwork = null; + } + } } } @@ -351,29 +355,20 @@ public class NetworkTimeUpdateService extends Binder { * Observer to watch for changes to the AUTO_TIME setting. It only triggers when the setting * is enabled. */ - private static class AutoTimeSettingObserver extends ContentObserver { + private class AutoTimeSettingObserver extends ContentObserver { private final Context mContext; - private final int mMsg; - private final Handler mHandler; - AutoTimeSettingObserver(@NonNull Context context, @NonNull Handler handler, int msg) { + AutoTimeSettingObserver(@NonNull Handler handler, @NonNull Context context) { super(handler); mContext = Objects.requireNonNull(context); - mHandler = Objects.requireNonNull(handler); - mMsg = msg; - } - - void observe() { - ContentResolver resolver = mContext.getContentResolver(); - resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME), - false, this); } @Override public void onChange(boolean selfChange) { + // onChange() will be invoked using handler, see the constructor. if (isAutomaticTimeEnabled()) { - mHandler.obtainMessage(mMsg).sendToTarget(); + onPollNetworkTime("automatic time enabled"); } } @@ -389,14 +384,19 @@ public class NetworkTimeUpdateService extends Binder { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - pw.println("mPollingIntervalMs=" + Duration.ofMillis(mPollingIntervalMs)); - pw.println("mPollingIntervalShorterMs=" + Duration.ofMillis(mPollingIntervalShorterMs)); + + pw.println("mNormalPollingIntervalMillis=" + + Duration.ofMillis(mNormalPollingIntervalMillis)); + pw.println("mShortPollingIntervalMillis=" + + Duration.ofMillis(mShortPollingIntervalMillis)); pw.println("mTryAgainTimesMax=" + mTryAgainTimesMax); - pw.println("mDefaultNetwork=" + mDefaultNetwork); - pw.println("mTryAgainCounter=" + mTryAgainCounter); + synchronized (mLock) { + pw.println("mDefaultNetwork=" + mDefaultNetwork); + pw.println("mTryAgainCounter=" + mTryAgainCounter); + } pw.println(); pw.println("NtpTrustedTime:"); - mTime.dump(pw); + mNtpTrustedTime.dump(pw); pw.println(); pw.println("Local logs:"); mLocalLog.dump(fd, pw, args);