diff options
| -rw-r--r-- | services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java | 84 | ||||
| -rw-r--r-- | services/core/java/com/android/server/timedetector/TimeDetectorService.java | 53 | ||||
| -rw-r--r-- | services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java | 10 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java (renamed from services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java) | 17 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java | 111 |
5 files changed, 165 insertions, 110 deletions
diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java index 3c79b2399fa3..0b970bfc0076 100644 --- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java @@ -23,10 +23,13 @@ import android.app.AlarmManager; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.Intent; +import android.util.LocalLog; import android.util.Slog; import android.util.TimestampedValue; +import com.android.internal.annotations.GuardedBy; import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.util.IndentingPrintWriter; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -34,13 +37,14 @@ import java.lang.annotation.RetentionPolicy; /** * 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). + * {@link AlarmManager}. + * + * <p>Most public methods are marked synchronized to ensure thread safety around internal state. */ -// @NotThreadSafe public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { - private final static String TAG = "timedetector.SimpleTimeDetectorStrategy"; + private static final boolean DBG = false; + private static final String LOG_TAG = "SimpleTimeDetectorStrategy"; @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL }) @Retention(RetentionPolicy.SOURCE) @@ -61,6 +65,9 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { */ private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000; + // A log for changes made to the system clock and why. + @NonNull private final LocalLog mTimeChangesLog = new LocalLog(30); + // @NonNull after initialize() private Callback mCallback; @@ -80,7 +87,7 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { } @Override - public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) { + public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) { // NITZ logic // Empty suggestions are just ignored as we don't currently keep track of suggestion origin. @@ -103,7 +110,7 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { } @Override - public void suggestManualTime(ManualTimeSuggestion timeSuggestion) { + public synchronized void suggestManualTime(ManualTimeSuggestion timeSuggestion) { final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime(); setSystemClockIfRequired(ORIGIN_MANUAL, newUtcTime, timeSuggestion); } @@ -116,7 +123,7 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { newSuggestion.getUtcTime(), lastSuggestion.getUtcTime()); if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) { // Out of order or bogus. - Slog.w(TAG, "validateNewNitzTime: Bad NITZ signal received." + Slog.w(LOG_TAG, "Bad NITZ signal received." + " referenceTimeDifference=" + referenceTimeDifference + " lastSuggestion=" + lastSuggestion + " newSuggestion=" + newSuggestion); @@ -126,6 +133,7 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { return true; } + @GuardedBy("this") private void setSystemClockIfRequired( @Origin int origin, TimestampedValue<Long> time, Object cause) { // Historically, Android has sent a TelephonyIntents.ACTION_NETWORK_SET_TIME broadcast only @@ -140,16 +148,20 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { mLastAutoSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast; if (!mCallback.isAutoTimeDetectionEnabled()) { - Slog.d(TAG, "setSystemClockIfRequired: Auto time detection is not enabled." - + " time=" + time - + ", cause=" + cause); + if (DBG) { + Slog.d(LOG_TAG, "Auto time detection is not enabled." + + " time=" + time + + ", cause=" + cause); + } return; } } else { if (mCallback.isAutoTimeDetectionEnabled()) { - Slog.d(TAG, "setSystemClockIfRequired: Auto time detection is enabled." - + " time=" + time - + ", cause=" + cause); + if (DBG) { + Slog.d(LOG_TAG, "Auto time detection is enabled." + + " time=" + time + + ", cause=" + cause); + } return; } } @@ -167,7 +179,7 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { mLastAutoSystemClockTimeSet, elapsedRealtimeMillis); long absSystemClockDifference = Math.abs(expectedTimeMillis - actualTimeMillis); if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) { - Slog.w(TAG, + Slog.w(LOG_TAG, "System clock has not tracked elapsed real time clock. A clock may" + " be inaccurate or something unexpectedly set the system" + " clock." @@ -190,9 +202,10 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { } @Override - public void handleAutoTimeDetectionToggle(boolean enabled) { + public synchronized void handleAutoTimeDetectionChanged() { // 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. + boolean enabled = mCallback.isAutoTimeDetectionEnabled(); if (enabled) { if (mLastAutoSystemClockTime != null) { // Only send the network broadcast if the last candidate would have caused one. @@ -218,14 +231,27 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { } @Override - public void dump(@NonNull PrintWriter pw, @Nullable String[] args) { + public synchronized void dump(@NonNull PrintWriter pw, @Nullable String[] args) { pw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion); pw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet); pw.println("mLastAutoSystemClockTime=" + mLastAutoSystemClockTime); pw.println("mLastAutoSystemClockTimeSendNetworkBroadcast=" + mLastAutoSystemClockTimeSendNetworkBroadcast); + + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + + ipw.println("TimeDetectorStrategyImpl logs:"); + ipw.increaseIndent(); // level 1 + + ipw.println("Time change log:"); + ipw.increaseIndent(); // level 2 + mTimeChangesLog.dump(ipw); + ipw.decreaseIndent(); // level 2 + + ipw.decreaseIndent(); // level 1 } + @GuardedBy("this") private void adjustAndSetDeviceSystemClock( TimestampedValue<Long> newTime, boolean sendNetworkBroadcast, long elapsedRealtimeMillis, long actualSystemClockMillis, Object cause) { @@ -238,21 +264,27 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { 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 - + " cause=" + cause - + " systemClockUpdateThreshold=" + systemClockUpdateThreshold - + " absTimeDifference=" + absTimeDifference); + if (DBG) { + Slog.d(LOG_TAG, "Not setting system clock. New time and" + + " system clock are close enough." + + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + + " newTime=" + newTime + + " cause=" + cause + + " systemClockUpdateThreshold=" + systemClockUpdateThreshold + + " absTimeDifference=" + absTimeDifference); + } return; } - Slog.d(TAG, "Setting system clock using time=" + newTime + mCallback.setSystemClock(newSystemClockMillis); + String logMsg = "Set system clock using time=" + newTime + " cause=" + cause + " elapsedRealtimeMillis=" + elapsedRealtimeMillis - + " newTimeMillis=" + newSystemClockMillis); - mCallback.setSystemClock(newSystemClockMillis); + + " newSystemClockMillis=" + newSystemClockMillis; + if (DBG) { + Slog.d(LOG_TAG, logMsg); + } + mTimeChangesLog.log(logMsg); // CLOCK_PARANOIA : Record the last time this class set the system clock. mLastAutoSystemClockTimeSet = newTime; diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index 7f5b4030306f..34400ff6b484 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -24,10 +24,9 @@ import android.app.timedetector.PhoneTimeSuggestion; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; -import android.os.Binder; +import android.os.Handler; 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; @@ -39,7 +38,7 @@ import java.io.PrintWriter; import java.util.Objects; public final class TimeDetectorService extends ITimeDetectorService.Stub { - private static final String TAG = "timedetector.TimeDetectorService"; + private static final String TAG = "TimeDetectorService"; public static class Lifecycle extends SystemService { @@ -57,29 +56,25 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { } } + @NonNull private final Handler mHandler; @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); + TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy(); + TimeDetectorStrategyCallbackImpl callback = new TimeDetectorStrategyCallbackImpl(context); timeDetector.initialize(callback); + Handler handler = FgThread.getHandler(); TimeDetectorService timeDetectorService = - new TimeDetectorService(context, callback, timeDetector); + new TimeDetectorService(context, handler, callback, timeDetector); // Wire up event listening. ContentResolver contentResolver = context.getContentResolver(); contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true, - new ContentObserver(FgThread.getHandler()) { + new ContentObserver(handler) { public void onChange(boolean selfChange) { timeDetectorService.handleAutoTimeDetectionToggle(); } @@ -89,9 +84,10 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { } @VisibleForTesting - public TimeDetectorService(@NonNull Context context, @NonNull Callback callback, - @NonNull TimeDetectorStrategy timeDetectorStrategy) { + public TimeDetectorService(@NonNull Context context, @NonNull Handler handler, + @NonNull Callback callback, @NonNull TimeDetectorStrategy timeDetectorStrategy) { mContext = Objects.requireNonNull(context); + mHandler = Objects.requireNonNull(handler); mCallback = Objects.requireNonNull(callback); mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy); } @@ -101,14 +97,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { enforceSuggestPhoneTimePermission(); Objects.requireNonNull(timeSignal); - long idToken = Binder.clearCallingIdentity(); - try { - synchronized (mStrategyLock) { - mTimeDetectorStrategy.suggestPhoneTime(timeSignal); - } - } finally { - Binder.restoreCallingIdentity(idToken); - } + mHandler.post(() -> mTimeDetectorStrategy.suggestPhoneTime(timeSignal)); } @Override @@ -116,22 +105,12 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { enforceSuggestManualTimePermission(); Objects.requireNonNull(timeSignal); - long idToken = Binder.clearCallingIdentity(); - try { - synchronized (mStrategyLock) { - mTimeDetectorStrategy.suggestManualTime(timeSignal); - } - } finally { - Binder.restoreCallingIdentity(idToken); - } + mHandler.post(() -> mTimeDetectorStrategy.suggestManualTime(timeSignal)); } @VisibleForTesting public void handleAutoTimeDetectionToggle() { - synchronized (mStrategyLock) { - final boolean timeDetectionEnabled = mCallback.isAutoTimeDetectionEnabled(); - mTimeDetectorStrategy.handleAutoTimeDetectionToggle(timeDetectionEnabled); - } + mHandler.post(mTimeDetectorStrategy::handleAutoTimeDetectionChanged); } @Override @@ -139,9 +118,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @Nullable String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - synchronized (mStrategyLock) { - mTimeDetectorStrategy.dump(pw, args); - } + mTimeDetectorStrategy.dump(pw, args); } private void enforceSuggestPhoneTimePermission() { diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index b60cebf57b45..32cee2de3d75 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -27,12 +27,14 @@ import java.io.PrintWriter; /** * The interface for classes that implement the time detection algorithm used by the - * 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). + * TimeDetectorService. + * + * <p>Most calls will be handled by a single thread but that is not true for all calls. For example + * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must + * handle thread safety. * * @hide */ -// @NotThreadSafe public interface TimeDetectorStrategy { /** @@ -79,7 +81,7 @@ public interface TimeDetectorStrategy { void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion); /** Handle the auto-time setting being toggled on or off. */ - void handleAutoTimeDetectionToggle(boolean enabled); + void handleAutoTimeDetectionChanged(); /** Dump debug information. */ void dump(@NonNull PrintWriter pw, @Nullable String[] args); diff --git a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java index 317fd4d566ae..8a7edf746340 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java @@ -40,7 +40,7 @@ import org.junit.runner.RunWith; import java.time.Duration; @RunWith(AndroidJUnit4.class) -public class SimpleTimeZoneDetectorStrategyTest { +public class SimpleTimeDetectorStrategyTest { private static final Scenario SCENARIO_1 = new Scenario.Builder() .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0) @@ -440,7 +440,7 @@ public class SimpleTimeZoneDetectorStrategyTest { mSystemClockMillis = systemClockMillis; } - public void pokeTimeDetectionEnabled(boolean enabled) { + public void pokeAutoTimeDetectionEnabled(boolean enabled) { mTimeDetectionEnabled = enabled; } @@ -457,6 +457,10 @@ public class SimpleTimeZoneDetectorStrategyTest { mSystemClockMillis += incrementMillis; } + public void simulateAutoTimeZoneDetectionToggle() { + mTimeDetectionEnabled = !mTimeDetectionEnabled; + } + public void verifySystemClockNotSet() { assertFalse(mSystemClockWasSet); } @@ -493,7 +497,7 @@ public class SimpleTimeZoneDetectorStrategyTest { private final FakeCallback mFakeCallback; private final SimpleTimeDetectorStrategy mSimpleTimeDetectorStrategy; - public Script() { + Script() { mFakeCallback = new FakeCallback(); mSimpleTimeDetectorStrategy = new SimpleTimeDetectorStrategy(); mSimpleTimeDetectorStrategy.initialize(mFakeCallback); @@ -501,7 +505,7 @@ public class SimpleTimeZoneDetectorStrategyTest { } Script pokeTimeDetectionEnabled(boolean enabled) { - mFakeCallback.pokeTimeDetectionEnabled(enabled); + mFakeCallback.pokeAutoTimeDetectionEnabled(enabled); return this; } @@ -535,9 +539,8 @@ public class SimpleTimeZoneDetectorStrategyTest { } Script simulateAutoTimeDetectionToggle() { - boolean enabled = !mFakeCallback.isAutoTimeDetectionEnabled(); - mFakeCallback.pokeTimeDetectionEnabled(enabled); - mSimpleTimeDetectorStrategy.handleAutoTimeDetectionToggle(enabled); + mFakeCallback.simulateAutoTimeZoneDetectionToggle(); + mSimpleTimeDetectorStrategy.handleAutoTimeDetectionChanged(); return this; } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index 4efe771a4e95..9951e8597a8b 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -17,13 +17,11 @@ package com.android.server.timedetector; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -32,12 +30,17 @@ import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.Context; import android.content.pm.PackageManager; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; import android.util.TimestampedValue; import androidx.test.runner.AndroidJUnit4; import com.android.server.timedetector.TimeDetectorStrategy.Callback; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -52,54 +55,62 @@ public class TimeDetectorServiceTest { private Callback mMockCallback; private TimeDetectorService mTimeDetectorService; + private HandlerThread mHandlerThread; + private TestHandler mTestHandler; + @Before public void setUp() { mMockContext = mock(Context.class); + + // Create a thread + handler for processing the work that the service posts. + mHandlerThread = new HandlerThread("TimeDetectorServiceTest"); + mHandlerThread.start(); + mTestHandler = new TestHandler(mHandlerThread.getLooper()); + mMockCallback = mock(Callback.class); mStubbedTimeDetectorStrategy = new StubbedTimeDetectorStrategy(); mTimeDetectorService = new TimeDetectorService( - mMockContext, mMockCallback, + mMockContext, mTestHandler, mMockCallback, mStubbedTimeDetectorStrategy); } - @Test(expected=SecurityException.class) - public void testStubbedCall_withoutPermission() { - doThrow(new SecurityException("Mock")) - .when(mMockContext).enforceCallingPermission(anyString(), any()); - PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion(); - - try { - mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion); - } finally { - verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.SET_TIME), anyString()); - } + @After + public void tearDown() throws Exception { + mHandlerThread.quit(); + mHandlerThread.join(); } @Test - public void testSuggestPhoneTime() { + public void testSuggestPhoneTime() throws Exception { doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion(); mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion); + mTestHandler.assertTotalMessagesEnqueued(1); + + verify(mMockContext).enforceCallingPermission( + eq(android.Manifest.permission.SET_TIME), + anyString()); - verify(mMockContext) - .enforceCallingPermission(eq(android.Manifest.permission.SET_TIME), anyString()); + mTestHandler.waitForEmptyQueue(); mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion); } @Test - public void testSuggestManualTime() { + public void testSuggestManualTime() throws Exception { doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion(); mTimeDetectorService.suggestManualTime(manualTimeSuggestion); + mTestHandler.assertTotalMessagesEnqueued(1); verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.SET_TIME), anyString()); + + mTestHandler.waitForEmptyQueue(); mStubbedTimeDetectorStrategy.verifySuggestManualTimeCalled(manualTimeSuggestion); } @@ -115,18 +126,16 @@ public class TimeDetectorServiceTest { } @Test - public void testAutoTimeDetectionToggle() { - when(mMockCallback.isAutoTimeDetectionEnabled()).thenReturn(true); - + public void testAutoTimeDetectionToggle() throws Exception { mTimeDetectorService.handleAutoTimeDetectionToggle(); - - mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(true); - - when(mMockCallback.isAutoTimeDetectionEnabled()).thenReturn(false); + mTestHandler.assertTotalMessagesEnqueued(1); + mTestHandler.waitForEmptyQueue(); + mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(); mTimeDetectorService.handleAutoTimeDetectionToggle(); - - mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(false); + mTestHandler.assertTotalMessagesEnqueued(2); + mTestHandler.waitForEmptyQueue(); + mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(); } private static PhoneTimeSuggestion createPhoneTimeSuggestion() { @@ -147,7 +156,7 @@ public class TimeDetectorServiceTest { // Call tracking. private PhoneTimeSuggestion mLastPhoneSuggestion; private ManualTimeSuggestion mLastManualSuggestion; - private Boolean mLastAutoTimeDetectionToggle; + private boolean mLastAutoTimeDetectionToggleCalled; private boolean mDumpCalled; @Override @@ -167,9 +176,9 @@ public class TimeDetectorServiceTest { } @Override - public void handleAutoTimeDetectionToggle(boolean enabled) { + public void handleAutoTimeDetectionChanged() { resetCallTracking(); - mLastAutoTimeDetectionToggle = enabled; + mLastAutoTimeDetectionToggleCalled = true; } @Override @@ -181,7 +190,7 @@ public class TimeDetectorServiceTest { void resetCallTracking() { mLastPhoneSuggestion = null; mLastManualSuggestion = null; - mLastAutoTimeDetectionToggle = null; + mLastAutoTimeDetectionToggleCalled = false; mDumpCalled = false; } @@ -193,13 +202,45 @@ public class TimeDetectorServiceTest { assertEquals(expectedSuggestion, mLastManualSuggestion); } - void verifyHandleAutoTimeDetectionToggleCalled(boolean expectedEnable) { - assertNotNull(mLastAutoTimeDetectionToggle); - assertEquals(expectedEnable, mLastAutoTimeDetectionToggle); + void verifyHandleAutoTimeDetectionToggleCalled() { + assertTrue(mLastAutoTimeDetectionToggleCalled); } void verifyDumpCalled() { assertTrue(mDumpCalled); } } + + /** + * A Handler that can track posts/sends and wait for work to be completed. + */ + private static class TestHandler extends Handler { + + private int mMessagesSent; + + TestHandler(Looper looper) { + super(looper); + } + + @Override + public boolean sendMessageAtTime(Message msg, long uptimeMillis) { + mMessagesSent++; + return super.sendMessageAtTime(msg, uptimeMillis); + } + + /** Asserts the number of messages posted or sent is as expected. */ + void assertTotalMessagesEnqueued(int expected) { + assertEquals(expected, mMessagesSent); + } + + /** + * Waits for all currently enqueued work due to be processed to be completed before + * returning. + */ + void waitForEmptyQueue() throws InterruptedException { + while (!getLooper().getQueue().isIdle()) { + Thread.sleep(100); + } + } + } } |