Marshal access to DreamOverlayStateController.
This changelist marshals calls on DreamoverlayStateController
on a single executor to ensure consistency and ordered
execution.
Bug: 211497162
Test: atest DreamOverlayStateController
Change-Id: If6870b9f1212bae845f39500b4d08925531f8de2
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index bc9bdfc..66679bb 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -17,15 +17,20 @@
package com.android.systemui.dreams;
import androidx.annotation.NonNull;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.policy.CallbackController;
+import com.google.common.util.concurrent.ListenableFuture;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Objects;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -77,12 +82,14 @@
}
}
+ private final Executor mExecutor;
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private final HashMap<ComplicationToken, ComplicationProvider> mComplications = new HashMap<>();
@VisibleForTesting
@Inject
- public DreamOverlayStateController() {
+ public DreamOverlayStateController(@Main Executor executor) {
+ mExecutor = executor;
}
/**
@@ -90,11 +97,16 @@
* @param provider The {@link ComplicationProvider} providing the dream.
* @return The {@link ComplicationToken} tied to the supplied {@link ComplicationProvider}.
*/
- public ComplicationToken addComplication(ComplicationProvider provider) {
- final ComplicationToken token = new ComplicationToken(mNextComplicationTokenId++);
- mComplications.put(token, provider);
- notifyCallbacks();
- return token;
+ public ListenableFuture<ComplicationToken> addComplication(ComplicationProvider provider) {
+ return CallbackToFutureAdapter.getFuture(completer -> {
+ mExecutor.execute(() -> {
+ final ComplicationToken token = new ComplicationToken(mNextComplicationTokenId++);
+ mComplications.put(token, provider);
+ notifyCallbacks();
+ completer.set(token);
+ });
+ return "DreamOverlayStateController::addComplication";
+ });
}
/**
@@ -103,14 +115,19 @@
* to be removed.
* @return The removed {@link ComplicationProvider}, {@code null} if not found.
*/
- public ComplicationProvider removeComplication(ComplicationToken token) {
- final ComplicationProvider removedComplication = mComplications.remove(token);
+ public ListenableFuture<ComplicationProvider> removeComplication(ComplicationToken token) {
+ return CallbackToFutureAdapter.getFuture(completer -> {
+ mExecutor.execute(() -> {
+ final ComplicationProvider removedComplication = mComplications.remove(token);
- if (removedComplication != null) {
- notifyCallbacks();
- }
+ if (removedComplication != null) {
+ notifyCallbacks();
+ }
+ completer.set(removedComplication);
+ });
- return removedComplication;
+ return "DreamOverlayStateController::removeComplication";
+ });
}
private void notifyCallbacks() {
@@ -121,24 +138,28 @@
@Override
public void addCallback(@NonNull Callback callback) {
- Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
- if (mCallbacks.contains(callback)) {
- return;
- }
+ mExecutor.execute(() -> {
+ Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
+ if (mCallbacks.contains(callback)) {
+ return;
+ }
- mCallbacks.add(callback);
+ mCallbacks.add(callback);
- if (mComplications.isEmpty()) {
- return;
- }
+ if (mComplications.isEmpty()) {
+ return;
+ }
- callback.onComplicationsChanged();
+ callback.onComplicationsChanged();
+ });
}
@Override
public void removeCallback(@NonNull Callback callback) {
- Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
- mCallbacks.remove(callback);
+ mExecutor.execute(() -> {
+ Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
+ mCallbacks.remove(callback);
+ });
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 1f4333e..efc3c7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -27,6 +27,10 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import com.google.common.util.concurrent.ListenableFuture;
import org.junit.Before;
import org.junit.Test;
@@ -45,20 +49,25 @@
@Mock
ComplicationProvider mProvider;
+ final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
- public void testCallback() {
- final DreamOverlayStateController stateController = new DreamOverlayStateController();
+ public void testCallback() throws Exception {
+ final DreamOverlayStateController stateController = new DreamOverlayStateController(
+ mExecutor);
stateController.addCallback(mCallback);
// Add complication and verify callback is notified.
- final DreamOverlayStateController.ComplicationToken token =
+ final ListenableFuture<DreamOverlayStateController.ComplicationToken> tokenFuture =
stateController.addComplication(mProvider);
+ mExecutor.runAllReady();
+
verify(mCallback, times(1)).onComplicationsChanged();
final Collection<ComplicationProvider> providers = stateController.getComplications();
@@ -68,19 +77,23 @@
clearInvocations(mCallback);
// Remove complication and verify callback is notified.
- stateController.removeComplication(token);
+ stateController.removeComplication(tokenFuture.get());
+ mExecutor.runAllReady();
verify(mCallback, times(1)).onComplicationsChanged();
assertTrue(providers.isEmpty());
}
@Test
public void testNotifyOnCallbackAdd() {
- final DreamOverlayStateController stateController = new DreamOverlayStateController();
- final DreamOverlayStateController.ComplicationToken token =
- stateController.addComplication(mProvider);
+ final DreamOverlayStateController stateController =
+ new DreamOverlayStateController(mExecutor);
+
+ stateController.addComplication(mProvider);
+ mExecutor.runAllReady();
// Verify callback occurs on add when an overlay is already present.
stateController.addCallback(mCallback);
+ mExecutor.runAllReady();
verify(mCallback, times(1)).onComplicationsChanged();
}
}