Loading core/java/android/util/TimestampedValue.java +8 −0 Original line number Diff line number Diff line Loading @@ -126,4 +126,12 @@ public final class TimestampedValue<T> { dest.writeLong(timestampedValue.mReferenceTimeMillis); dest.writeValue(timestampedValue.mValue); } /** * Returns the difference in milliseconds between two instance's reference times. */ public static long referenceTimeDifference( @NonNull TimestampedValue<?> one, @NonNull TimestampedValue<?> two) { return one.mReferenceTimeMillis - two.mReferenceTimeMillis; } } core/tests/coretests/src/android/util/TimestampedValueTest.java +10 −0 Original line number Diff line number Diff line Loading @@ -116,4 +116,14 @@ public class TimestampedValueTest { parcel.recycle(); } } @Test public void testReferenceTimeDifference() { TimestampedValue<Long> value1 = new TimestampedValue<>(1000, 123L); assertEquals(0, TimestampedValue.referenceTimeDifference(value1, value1)); TimestampedValue<Long> value2 = new TimestampedValue<>(1, 321L); assertEquals(999, TimestampedValue.referenceTimeDifference(value1, value2)); assertEquals(-999, TimestampedValue.referenceTimeDifference(value2, value1)); } } services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java +184 −9 Original line number Diff line number Diff line Loading @@ -20,38 +20,213 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.timedetector.TimeSignal; import android.content.Intent; import android.util.Slog; import android.util.TimestampedValue; import com.android.internal.telephony.TelephonyIntents; import java.io.FileDescriptor; import java.io.PrintWriter; /** * A placeholder implementation of TimeDetectorStrategy that passes NITZ suggestions immediately * to {@link AlarmManager}. * An implementation of TimeDetectorStrategy that passes only NITZ suggestions to * {@link AlarmManager}. The TimeDetectorService handles thread safety: all calls to * this class can be assumed to be single threaded (though the thread used may vary). */ // @NotThreadSafe public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { private final static String TAG = "timedetector.SimpleTimeDetectorStrategy"; private Callback mHelper; /** * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the * actual system clock time before a warning is logged. Used to help identify situations where * there is something other than this class setting the system clock. */ private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000; // @NonNull after initialize() private Callback mCallback; // NITZ state. @Nullable private TimestampedValue<Long> mLastNitzTime; // Information about the last time signal received: Used when toggling auto-time. @Nullable private TimestampedValue<Long> mLastSystemClockTime; private boolean mLastSystemClockTimeSendNetworkBroadcast; // System clock state. @Nullable private TimestampedValue<Long> mLastSystemClockTimeSet; @Override public void initialize(@NonNull Callback callback) { mHelper = callback; mCallback = callback; } @Override public void suggestTime(@NonNull TimeSignal timeSignal) { if (!TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId())) { Slog.w(TAG, "Ignoring signal from unknown source: " + timeSignal); Slog.w(TAG, "Ignoring signal from unsupported source: " + timeSignal); return; } mHelper.setTime(timeSignal.getUtcTime()); // NITZ logic TimestampedValue<Long> newNitzUtcTime = timeSignal.getUtcTime(); boolean nitzTimeIsValid = validateNewNitzTime(newNitzUtcTime, mLastNitzTime); if (!nitzTimeIsValid) { return; } // Always store the last NITZ value received, regardless of whether we go on to use it to // update the system clock. This is so that we can validate future NITZ signals. mLastNitzTime = newNitzUtcTime; // System clock update logic. // Historically, Android has sent a telephony broadcast only when setting the time using // NITZ. final boolean sendNetworkBroadcast = TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId()); final TimestampedValue<Long> newUtcTime = newNitzUtcTime; setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast); } private static boolean validateNewNitzTime(TimestampedValue<Long> newNitzUtcTime, TimestampedValue<Long> lastNitzTime) { if (lastNitzTime != null) { long referenceTimeDifference = TimestampedValue.referenceTimeDifference(newNitzUtcTime, lastNitzTime); if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) { // Out of order or bogus. Slog.w(TAG, "validateNewNitzTime: Bad NITZ signal received." + " referenceTimeDifference=" + referenceTimeDifference + " lastNitzTime=" + lastNitzTime + " newNitzUtcTime=" + newNitzUtcTime); return false; } } return true; } private void setSystemClockIfRequired( TimestampedValue<Long> time, boolean sendNetworkBroadcast) { // Store the last candidate we've seen in all cases so we can set the system clock // when/if time detection is enabled. mLastSystemClockTime = time; mLastSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast; if (!mCallback.isTimeDetectionEnabled()) { Slog.d(TAG, "setSystemClockIfRequired: Time detection is not enabled. time=" + time); return; } mCallback.acquireWakeLock(); try { long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis(); long actualTimeMillis = mCallback.systemClockMillis(); // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else // may be setting the clock. if (mLastSystemClockTimeSet != null) { long expectedTimeMillis = TimeDetectorStrategy.getTimeAt( mLastSystemClockTimeSet, elapsedRealtimeMillis); long absSystemClockDifference = Math.abs(expectedTimeMillis - actualTimeMillis); if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) { Slog.w(TAG, "System clock has not tracked elapsed real time clock. A clock may" + " be inaccurate or something unexpectedly set the system clock." + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " expectedTimeMillis=" + expectedTimeMillis + " actualTimeMillis=" + actualTimeMillis); } } final String reason = "New time signal"; adjustAndSetDeviceSystemClock( time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, reason); } finally { mCallback.releaseWakeLock(); } } @Override public void handleAutoTimeDetectionToggle(boolean enabled) { // If automatic time detection is enabled we update the system clock instantly if we can. // Conversely, if automatic time detection is disabled we leave the clock as it is. if (enabled) { if (mLastSystemClockTime != null) { // Only send the network broadcast if the last candidate would have caused one. final boolean sendNetworkBroadcast = mLastSystemClockTimeSendNetworkBroadcast; mCallback.acquireWakeLock(); try { long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis(); long actualTimeMillis = mCallback.systemClockMillis(); final String reason = "Automatic time detection enabled."; adjustAndSetDeviceSystemClock(mLastSystemClockTime, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, reason); } finally { mCallback.releaseWakeLock(); } } } else { // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what // it should be in future. mLastSystemClockTimeSet = null; } } @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args) { // No state to dump. public void dump(@NonNull PrintWriter pw, @Nullable String[] args) { pw.println("mLastNitzTime=" + mLastNitzTime); pw.println("mLastSystemClockTimeSet=" + mLastSystemClockTimeSet); pw.println("mLastSystemClockTime=" + mLastSystemClockTime); pw.println("mLastSystemClockTimeSendNetworkBroadcast=" + mLastSystemClockTimeSendNetworkBroadcast); } private void adjustAndSetDeviceSystemClock( TimestampedValue<Long> newTime, boolean sendNetworkBroadcast, long elapsedRealtimeMillis, long actualSystemClockMillis, String reason) { // Adjust for the time that has elapsed since the signal was received. long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis); // Check if the new signal would make sufficient difference to the system clock. If it's // below the threshold then ignore it. long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis); long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis(); if (absTimeDifference < systemClockUpdateThreshold) { Slog.d(TAG, "adjustAndSetDeviceSystemClock: Not setting system clock. New time and" + " system clock are close enough." + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " newTime=" + newTime + " reason=" + reason + " systemClockUpdateThreshold=" + systemClockUpdateThreshold + " absTimeDifference=" + absTimeDifference); return; } Slog.d(TAG, "Setting system clock using time=" + newTime + " reason=" + reason + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " newTimeMillis=" + newSystemClockMillis); mCallback.setSystemClock(newSystemClockMillis); // CLOCK_PARANOIA : Record the last time this class set the system clock. mLastSystemClockTimeSet = newTime; if (sendNetworkBroadcast) { // Send a broadcast that telephony code used to send after setting the clock. // TODO Remove this broadcast as soon as there are no remaining listeners. Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra("time", newSystemClockMillis); mCallback.sendStickyBroadcast(intent); } } } services/core/java/com/android/server/timedetector/TimeDetectorService.java +54 −13 Original line number Diff line number Diff line Loading @@ -20,24 +20,29 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.timedetector.ITimeDetectorService; import android.app.timedetector.TimeSignal; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.os.Binder; import android.provider.Settings; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.server.FgThread; import com.android.server.SystemService; import com.android.server.timedetector.TimeDetectorStrategy.Callback; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Objects; public final class TimeDetectorService extends ITimeDetectorService.Stub { private static final String TAG = "timedetector.TimeDetectorService"; public static class Lifecycle extends SystemService { public Lifecycle(Context context) { public Lifecycle(@NonNull Context context) { super(context); } Loading @@ -51,31 +56,65 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { } } private final Context mContext; private final TimeDetectorStrategy mTimeDetectorStrategy; @NonNull private final Context mContext; @NonNull private final Callback mCallback; // The lock used when call the strategy to ensure thread safety. @NonNull private final Object mStrategyLock = new Object(); @GuardedBy("mStrategyLock") @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy; private static TimeDetectorService create(@NonNull Context context) { final TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy(); final TimeDetectorStrategyCallbackImpl callback = new TimeDetectorStrategyCallbackImpl(context); timeDetector.initialize(callback); TimeDetectorService timeDetectorService = new TimeDetectorService(context, callback, timeDetector); // Wire up event listening. ContentResolver contentResolver = context.getContentResolver(); contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true, new ContentObserver(FgThread.getHandler()) { public void onChange(boolean selfChange) { timeDetectorService.handleAutoTimeDetectionToggle(); } }); private static TimeDetectorService create(Context context) { TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy(); timeDetector.initialize(new TimeDetectorStrategyCallbackImpl(context)); return new TimeDetectorService(context, timeDetector); return timeDetectorService; } @VisibleForTesting public TimeDetectorService(@NonNull Context context, public TimeDetectorService(@NonNull Context context, @NonNull Callback callback, @NonNull TimeDetectorStrategy timeDetectorStrategy) { mContext = Objects.requireNonNull(context); mCallback = Objects.requireNonNull(callback); mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy); } @Override public void suggestTime(@NonNull TimeSignal timeSignal) { enforceSetTimePermission(); Objects.requireNonNull(timeSignal); long callerIdToken = Binder.clearCallingIdentity(); long idToken = Binder.clearCallingIdentity(); try { synchronized (mStrategyLock) { mTimeDetectorStrategy.suggestTime(timeSignal); } } finally { Binder.restoreCallingIdentity(callerIdToken); Binder.restoreCallingIdentity(idToken); } } @VisibleForTesting public void handleAutoTimeDetectionToggle() { synchronized (mStrategyLock) { final boolean timeDetectionEnabled = mCallback.isTimeDetectionEnabled(); mTimeDetectorStrategy.handleAutoTimeDetectionToggle(timeDetectionEnabled); } } Loading @@ -84,7 +123,9 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @Nullable String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; mTimeDetectorStrategy.dump(fd, pw, args); synchronized (mStrategyLock) { mTimeDetectorStrategy.dump(pw, args); } } private void enforceSetTimePermission() { Loading services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +44 −4 Original line number Diff line number Diff line Loading @@ -19,26 +19,66 @@ package com.android.server.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.timedetector.TimeSignal; import android.content.Intent; import android.util.TimestampedValue; import java.io.FileDescriptor; import java.io.PrintWriter; /** * The interface for classes that implement the time detection algorithm used by the * TimeDetectorService. * TimeDetectorService. The TimeDetectorService handles thread safety: all calls to implementations * of this interface can be assumed to be single threaded (though the thread used may vary). * * @hide */ // @NotThreadSafe public interface TimeDetectorStrategy { /** * The interface used by the strategy to interact with the surrounding service. */ interface Callback { void setTime(TimestampedValue<Long> time); /** * The absolute threshold below which the system clock need not be updated. i.e. if setting * the system clock would adjust it by less than this (either backwards or forwards) then it * need not be set. */ int systemClockUpdateThresholdMillis(); /** Returns true if automatic time detection is enabled. */ boolean isTimeDetectionEnabled(); /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */ void acquireWakeLock(); /** Returns the elapsedRealtimeMillis clock value. The WakeLock must be held. */ long elapsedRealtimeMillis(); /** Returns the system clock value. The WakeLock must be held. */ long systemClockMillis(); /** Sets the device system clock. The WakeLock must be held. */ void setSystemClock(long newTimeMillis); /** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */ void releaseWakeLock(); /** Send the supplied intent as a stick broadcast. */ void sendStickyBroadcast(@NonNull Intent intent); } /** Initialize the strategy. */ void initialize(@NonNull Callback callback); /** Process the suggested time. */ void suggestTime(@NonNull TimeSignal timeSignal); void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args); /** Handle the auto-time setting being toggled on or off. */ void handleAutoTimeDetectionToggle(boolean enabled); /** Dump debug information. */ void dump(@NonNull PrintWriter pw, @Nullable String[] args); // Utility methods below are to be moved to a better home when one becomes more obvious. Loading Loading
core/java/android/util/TimestampedValue.java +8 −0 Original line number Diff line number Diff line Loading @@ -126,4 +126,12 @@ public final class TimestampedValue<T> { dest.writeLong(timestampedValue.mReferenceTimeMillis); dest.writeValue(timestampedValue.mValue); } /** * Returns the difference in milliseconds between two instance's reference times. */ public static long referenceTimeDifference( @NonNull TimestampedValue<?> one, @NonNull TimestampedValue<?> two) { return one.mReferenceTimeMillis - two.mReferenceTimeMillis; } }
core/tests/coretests/src/android/util/TimestampedValueTest.java +10 −0 Original line number Diff line number Diff line Loading @@ -116,4 +116,14 @@ public class TimestampedValueTest { parcel.recycle(); } } @Test public void testReferenceTimeDifference() { TimestampedValue<Long> value1 = new TimestampedValue<>(1000, 123L); assertEquals(0, TimestampedValue.referenceTimeDifference(value1, value1)); TimestampedValue<Long> value2 = new TimestampedValue<>(1, 321L); assertEquals(999, TimestampedValue.referenceTimeDifference(value1, value2)); assertEquals(-999, TimestampedValue.referenceTimeDifference(value2, value1)); } }
services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java +184 −9 Original line number Diff line number Diff line Loading @@ -20,38 +20,213 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.timedetector.TimeSignal; import android.content.Intent; import android.util.Slog; import android.util.TimestampedValue; import com.android.internal.telephony.TelephonyIntents; import java.io.FileDescriptor; import java.io.PrintWriter; /** * A placeholder implementation of TimeDetectorStrategy that passes NITZ suggestions immediately * to {@link AlarmManager}. * An implementation of TimeDetectorStrategy that passes only NITZ suggestions to * {@link AlarmManager}. The TimeDetectorService handles thread safety: all calls to * this class can be assumed to be single threaded (though the thread used may vary). */ // @NotThreadSafe public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { private final static String TAG = "timedetector.SimpleTimeDetectorStrategy"; private Callback mHelper; /** * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the * actual system clock time before a warning is logged. Used to help identify situations where * there is something other than this class setting the system clock. */ private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000; // @NonNull after initialize() private Callback mCallback; // NITZ state. @Nullable private TimestampedValue<Long> mLastNitzTime; // Information about the last time signal received: Used when toggling auto-time. @Nullable private TimestampedValue<Long> mLastSystemClockTime; private boolean mLastSystemClockTimeSendNetworkBroadcast; // System clock state. @Nullable private TimestampedValue<Long> mLastSystemClockTimeSet; @Override public void initialize(@NonNull Callback callback) { mHelper = callback; mCallback = callback; } @Override public void suggestTime(@NonNull TimeSignal timeSignal) { if (!TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId())) { Slog.w(TAG, "Ignoring signal from unknown source: " + timeSignal); Slog.w(TAG, "Ignoring signal from unsupported source: " + timeSignal); return; } mHelper.setTime(timeSignal.getUtcTime()); // NITZ logic TimestampedValue<Long> newNitzUtcTime = timeSignal.getUtcTime(); boolean nitzTimeIsValid = validateNewNitzTime(newNitzUtcTime, mLastNitzTime); if (!nitzTimeIsValid) { return; } // Always store the last NITZ value received, regardless of whether we go on to use it to // update the system clock. This is so that we can validate future NITZ signals. mLastNitzTime = newNitzUtcTime; // System clock update logic. // Historically, Android has sent a telephony broadcast only when setting the time using // NITZ. final boolean sendNetworkBroadcast = TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId()); final TimestampedValue<Long> newUtcTime = newNitzUtcTime; setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast); } private static boolean validateNewNitzTime(TimestampedValue<Long> newNitzUtcTime, TimestampedValue<Long> lastNitzTime) { if (lastNitzTime != null) { long referenceTimeDifference = TimestampedValue.referenceTimeDifference(newNitzUtcTime, lastNitzTime); if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) { // Out of order or bogus. Slog.w(TAG, "validateNewNitzTime: Bad NITZ signal received." + " referenceTimeDifference=" + referenceTimeDifference + " lastNitzTime=" + lastNitzTime + " newNitzUtcTime=" + newNitzUtcTime); return false; } } return true; } private void setSystemClockIfRequired( TimestampedValue<Long> time, boolean sendNetworkBroadcast) { // Store the last candidate we've seen in all cases so we can set the system clock // when/if time detection is enabled. mLastSystemClockTime = time; mLastSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast; if (!mCallback.isTimeDetectionEnabled()) { Slog.d(TAG, "setSystemClockIfRequired: Time detection is not enabled. time=" + time); return; } mCallback.acquireWakeLock(); try { long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis(); long actualTimeMillis = mCallback.systemClockMillis(); // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else // may be setting the clock. if (mLastSystemClockTimeSet != null) { long expectedTimeMillis = TimeDetectorStrategy.getTimeAt( mLastSystemClockTimeSet, elapsedRealtimeMillis); long absSystemClockDifference = Math.abs(expectedTimeMillis - actualTimeMillis); if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) { Slog.w(TAG, "System clock has not tracked elapsed real time clock. A clock may" + " be inaccurate or something unexpectedly set the system clock." + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " expectedTimeMillis=" + expectedTimeMillis + " actualTimeMillis=" + actualTimeMillis); } } final String reason = "New time signal"; adjustAndSetDeviceSystemClock( time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, reason); } finally { mCallback.releaseWakeLock(); } } @Override public void handleAutoTimeDetectionToggle(boolean enabled) { // If automatic time detection is enabled we update the system clock instantly if we can. // Conversely, if automatic time detection is disabled we leave the clock as it is. if (enabled) { if (mLastSystemClockTime != null) { // Only send the network broadcast if the last candidate would have caused one. final boolean sendNetworkBroadcast = mLastSystemClockTimeSendNetworkBroadcast; mCallback.acquireWakeLock(); try { long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis(); long actualTimeMillis = mCallback.systemClockMillis(); final String reason = "Automatic time detection enabled."; adjustAndSetDeviceSystemClock(mLastSystemClockTime, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, reason); } finally { mCallback.releaseWakeLock(); } } } else { // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what // it should be in future. mLastSystemClockTimeSet = null; } } @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args) { // No state to dump. public void dump(@NonNull PrintWriter pw, @Nullable String[] args) { pw.println("mLastNitzTime=" + mLastNitzTime); pw.println("mLastSystemClockTimeSet=" + mLastSystemClockTimeSet); pw.println("mLastSystemClockTime=" + mLastSystemClockTime); pw.println("mLastSystemClockTimeSendNetworkBroadcast=" + mLastSystemClockTimeSendNetworkBroadcast); } private void adjustAndSetDeviceSystemClock( TimestampedValue<Long> newTime, boolean sendNetworkBroadcast, long elapsedRealtimeMillis, long actualSystemClockMillis, String reason) { // Adjust for the time that has elapsed since the signal was received. long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis); // Check if the new signal would make sufficient difference to the system clock. If it's // below the threshold then ignore it. long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis); long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis(); if (absTimeDifference < systemClockUpdateThreshold) { Slog.d(TAG, "adjustAndSetDeviceSystemClock: Not setting system clock. New time and" + " system clock are close enough." + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " newTime=" + newTime + " reason=" + reason + " systemClockUpdateThreshold=" + systemClockUpdateThreshold + " absTimeDifference=" + absTimeDifference); return; } Slog.d(TAG, "Setting system clock using time=" + newTime + " reason=" + reason + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " newTimeMillis=" + newSystemClockMillis); mCallback.setSystemClock(newSystemClockMillis); // CLOCK_PARANOIA : Record the last time this class set the system clock. mLastSystemClockTimeSet = newTime; if (sendNetworkBroadcast) { // Send a broadcast that telephony code used to send after setting the clock. // TODO Remove this broadcast as soon as there are no remaining listeners. Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra("time", newSystemClockMillis); mCallback.sendStickyBroadcast(intent); } } }
services/core/java/com/android/server/timedetector/TimeDetectorService.java +54 −13 Original line number Diff line number Diff line Loading @@ -20,24 +20,29 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.timedetector.ITimeDetectorService; import android.app.timedetector.TimeSignal; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.os.Binder; import android.provider.Settings; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.server.FgThread; import com.android.server.SystemService; import com.android.server.timedetector.TimeDetectorStrategy.Callback; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Objects; public final class TimeDetectorService extends ITimeDetectorService.Stub { private static final String TAG = "timedetector.TimeDetectorService"; public static class Lifecycle extends SystemService { public Lifecycle(Context context) { public Lifecycle(@NonNull Context context) { super(context); } Loading @@ -51,31 +56,65 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { } } private final Context mContext; private final TimeDetectorStrategy mTimeDetectorStrategy; @NonNull private final Context mContext; @NonNull private final Callback mCallback; // The lock used when call the strategy to ensure thread safety. @NonNull private final Object mStrategyLock = new Object(); @GuardedBy("mStrategyLock") @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy; private static TimeDetectorService create(@NonNull Context context) { final TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy(); final TimeDetectorStrategyCallbackImpl callback = new TimeDetectorStrategyCallbackImpl(context); timeDetector.initialize(callback); TimeDetectorService timeDetectorService = new TimeDetectorService(context, callback, timeDetector); // Wire up event listening. ContentResolver contentResolver = context.getContentResolver(); contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true, new ContentObserver(FgThread.getHandler()) { public void onChange(boolean selfChange) { timeDetectorService.handleAutoTimeDetectionToggle(); } }); private static TimeDetectorService create(Context context) { TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy(); timeDetector.initialize(new TimeDetectorStrategyCallbackImpl(context)); return new TimeDetectorService(context, timeDetector); return timeDetectorService; } @VisibleForTesting public TimeDetectorService(@NonNull Context context, public TimeDetectorService(@NonNull Context context, @NonNull Callback callback, @NonNull TimeDetectorStrategy timeDetectorStrategy) { mContext = Objects.requireNonNull(context); mCallback = Objects.requireNonNull(callback); mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy); } @Override public void suggestTime(@NonNull TimeSignal timeSignal) { enforceSetTimePermission(); Objects.requireNonNull(timeSignal); long callerIdToken = Binder.clearCallingIdentity(); long idToken = Binder.clearCallingIdentity(); try { synchronized (mStrategyLock) { mTimeDetectorStrategy.suggestTime(timeSignal); } } finally { Binder.restoreCallingIdentity(callerIdToken); Binder.restoreCallingIdentity(idToken); } } @VisibleForTesting public void handleAutoTimeDetectionToggle() { synchronized (mStrategyLock) { final boolean timeDetectionEnabled = mCallback.isTimeDetectionEnabled(); mTimeDetectorStrategy.handleAutoTimeDetectionToggle(timeDetectionEnabled); } } Loading @@ -84,7 +123,9 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @Nullable String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; mTimeDetectorStrategy.dump(fd, pw, args); synchronized (mStrategyLock) { mTimeDetectorStrategy.dump(pw, args); } } private void enforceSetTimePermission() { Loading
services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +44 −4 Original line number Diff line number Diff line Loading @@ -19,26 +19,66 @@ package com.android.server.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.timedetector.TimeSignal; import android.content.Intent; import android.util.TimestampedValue; import java.io.FileDescriptor; import java.io.PrintWriter; /** * The interface for classes that implement the time detection algorithm used by the * TimeDetectorService. * TimeDetectorService. The TimeDetectorService handles thread safety: all calls to implementations * of this interface can be assumed to be single threaded (though the thread used may vary). * * @hide */ // @NotThreadSafe public interface TimeDetectorStrategy { /** * The interface used by the strategy to interact with the surrounding service. */ interface Callback { void setTime(TimestampedValue<Long> time); /** * The absolute threshold below which the system clock need not be updated. i.e. if setting * the system clock would adjust it by less than this (either backwards or forwards) then it * need not be set. */ int systemClockUpdateThresholdMillis(); /** Returns true if automatic time detection is enabled. */ boolean isTimeDetectionEnabled(); /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */ void acquireWakeLock(); /** Returns the elapsedRealtimeMillis clock value. The WakeLock must be held. */ long elapsedRealtimeMillis(); /** Returns the system clock value. The WakeLock must be held. */ long systemClockMillis(); /** Sets the device system clock. The WakeLock must be held. */ void setSystemClock(long newTimeMillis); /** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */ void releaseWakeLock(); /** Send the supplied intent as a stick broadcast. */ void sendStickyBroadcast(@NonNull Intent intent); } /** Initialize the strategy. */ void initialize(@NonNull Callback callback); /** Process the suggested time. */ void suggestTime(@NonNull TimeSignal timeSignal); void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args); /** Handle the auto-time setting being toggled on or off. */ void handleAutoTimeDetectionToggle(boolean enabled); /** Dump debug information. */ void dump(@NonNull PrintWriter pw, @Nullable String[] args); // Utility methods below are to be moved to a better home when one becomes more obvious. Loading