Merge "Make TimeDetectorService / Strategy consistent" am: 77d5009778
am: 8e08a832dc
Change-Id: Iefabfdf89096914ae99b2117901730a800dc1c6f
diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
index 3c79b23..0b970bf 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.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 @@
/**
* 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 @@
*/
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 @@
}
@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 @@
}
@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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 @@
}
@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 @@
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 7f5b403..34400ff 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.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.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 @@
}
}
+ @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 @@
}
@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 @@
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 @@
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 @@
@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 b60cebf..32cee2d 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 @@
/**
* 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 @@
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
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
rename to services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
index 317fd4d..8a7edf7 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 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 @@
mSystemClockMillis = systemClockMillis;
}
- public void pokeTimeDetectionEnabled(boolean enabled) {
+ public void pokeAutoTimeDetectionEnabled(boolean enabled) {
mTimeDetectionEnabled = enabled;
}
@@ -457,6 +457,10 @@
mSystemClockMillis += incrementMillis;
}
+ public void simulateAutoTimeZoneDetectionToggle() {
+ mTimeDetectionEnabled = !mTimeDetectionEnabled;
+ }
+
public void verifySystemClockNotSet() {
assertFalse(mSystemClockWasSet);
}
@@ -493,7 +497,7 @@
private final FakeCallback mFakeCallback;
private final SimpleTimeDetectorStrategy mSimpleTimeDetectorStrategy;
- public Script() {
+ Script() {
mFakeCallback = new FakeCallback();
mSimpleTimeDetectorStrategy = new SimpleTimeDetectorStrategy();
mSimpleTimeDetectorStrategy.initialize(mFakeCallback);
@@ -501,7 +505,7 @@
}
Script pokeTimeDetectionEnabled(boolean enabled) {
- mFakeCallback.pokeTimeDetectionEnabled(enabled);
+ mFakeCallback.pokeAutoTimeDetectionEnabled(enabled);
return this;
}
@@ -535,9 +539,8 @@
}
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 4efe771..9951e85 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.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 @@
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);
-
- verify(mMockContext)
- .enforceCallingPermission(eq(android.Manifest.permission.SET_TIME), anyString());
- mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
- }
-
- @Test
- public void testSuggestManualTime() {
- 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.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
+ }
+
+ @Test
+ 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 @@
}
@Test
- public void testAutoTimeDetectionToggle() {
- when(mMockCallback.isAutoTimeDetectionEnabled()).thenReturn(true);
+ public void testAutoTimeDetectionToggle() throws Exception {
+ mTimeDetectorService.handleAutoTimeDetectionToggle();
+ mTestHandler.assertTotalMessagesEnqueued(1);
+ mTestHandler.waitForEmptyQueue();
+ mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled();
mTimeDetectorService.handleAutoTimeDetectionToggle();
-
- mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(true);
-
- when(mMockCallback.isAutoTimeDetectionEnabled()).thenReturn(false);
-
- mTimeDetectorService.handleAutoTimeDetectionToggle();
-
- mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(false);
+ mTestHandler.assertTotalMessagesEnqueued(2);
+ mTestHandler.waitForEmptyQueue();
+ mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled();
}
private static PhoneTimeSuggestion createPhoneTimeSuggestion() {
@@ -147,7 +156,7 @@
// Call tracking.
private PhoneTimeSuggestion mLastPhoneSuggestion;
private ManualTimeSuggestion mLastManualSuggestion;
- private Boolean mLastAutoTimeDetectionToggle;
+ private boolean mLastAutoTimeDetectionToggleCalled;
private boolean mDumpCalled;
@Override
@@ -167,9 +176,9 @@
}
@Override
- public void handleAutoTimeDetectionToggle(boolean enabled) {
+ public void handleAutoTimeDetectionChanged() {
resetCallTracking();
- mLastAutoTimeDetectionToggle = enabled;
+ mLastAutoTimeDetectionToggleCalled = true;
}
@Override
@@ -181,7 +190,7 @@
void resetCallTracking() {
mLastPhoneSuggestion = null;
mLastManualSuggestion = null;
- mLastAutoTimeDetectionToggle = null;
+ mLastAutoTimeDetectionToggleCalled = false;
mDumpCalled = false;
}
@@ -193,13 +202,45 @@
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);
+ }
+ }
+ }
}