From 0aa0f4acb29b4b61d66aabc609d0be3ced06aeeb Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Fri, 6 Jan 2023 00:53:28 +0000 Subject: [PATCH] Remove Dream Overlay when Dream Window detaches. This changelist removes the dream overlay window when the associated dream window is removed. This follows closely to the attach behavior, where the dream overlay window is added after the dream window is attached. Previously, the dream overlay window was removed during onDestroy. Deferring removal to this later point in the lifecycle allows for the window to be incorrectly acted upon after the associated dream is gone. This changelist also implements a default implementation of DreamOverlayService#onWakeUp which immediately runs the callback. It also addresses an issue where service connection consumers were not cleared in the overlay service connection. Fixes: 261913526 Test: atest DreamOverlayServiceTest Test: atest DreamOverlayTest Change-Id: I4c80b585a484c20a8282fca75bd1908b494ce928 --- core/api/test-current.txt | 1 + .../service/dreams/DreamOverlayService.java | 16 +++++++++- .../android/service/dreams/DreamService.java | 30 ++++++++++++++----- .../android/service/dreams/IDreamOverlay.aidl | 3 ++ .../util/ObservableServiceConnection.java | 7 +++++ .../systemui/dreams/DreamOverlayService.java | 8 +++++ .../dreams/DreamOverlayServiceTest.java | 26 ++++++++++++++++ 7 files changed, 82 insertions(+), 9 deletions(-) diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 70a23cdf106ba..1a9e75b31e626 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2412,6 +2412,7 @@ package android.service.dreams { public abstract class DreamOverlayService extends android.app.Service { ctor public DreamOverlayService(); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); + method public void onEndDream(); method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams); method public final void requestExit(); method public final boolean shouldShowComplications(); diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java index 6e8198ba0cd1a..bf5b970311d0d 100644 --- a/core/java/android/service/dreams/DreamOverlayService.java +++ b/core/java/android/service/dreams/DreamOverlayService.java @@ -50,6 +50,11 @@ public abstract class DreamOverlayService extends Service { onStartDream(layoutParams); } + @Override + public void endDream() { + onEndDream(); + } + @Override public void wakeUp() { onWakeUp(() -> { @@ -83,13 +88,22 @@ public abstract class DreamOverlayService extends Service { /** * This method is overridden by implementations to handle when the dream has been requested - * to wakeup. This allows any overlay animations to run. + * to wakeup. This allows any overlay animations to run. By default, the method will invoke + * the callback immediately. * * @param onCompleteCallback The callback to trigger to notify the dream service that the * overlay has completed waking up. * @hide */ public void onWakeUp(@NonNull Runnable onCompleteCallback) { + onCompleteCallback.run(); + } + + /** + * This method is overridden by implementations to handle when the dream has ended. There may + * be earlier signals leading up to this step, such as @{@link #onWakeUp(Runnable)}. + */ + public void onEndDream() { } /** diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 8b9852a8f1b79..d3788862b6c06 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -297,14 +297,20 @@ public class DreamService extends Service implements Window.Callback { } public void addConsumer(Consumer consumer) { - mConsumers.add(consumer); - if (mOverlay != null) { - consumer.accept(mOverlay); - } + execute(() -> { + mConsumers.add(consumer); + if (mOverlay != null) { + consumer.accept(mOverlay); + } + }); } public void removeConsumer(Consumer consumer) { - mConsumers.remove(consumer); + execute(() -> mConsumers.remove(consumer)); + } + + public void clearConsumers() { + execute(() -> mConsumers.clear()); } } @@ -1383,6 +1389,17 @@ public class DreamService extends Service implements Window.Callback { @Override public void onViewDetachedFromWindow(View v) { + if (mOverlayConnection != null) { + mOverlayConnection.addConsumer(overlay -> { + try { + overlay.endDream(); + } catch (RemoteException e) { + Log.e(mTag, "could not inform overlay of dream end:" + e); + } + }); + mOverlayConnection.clearConsumers(); + } + if (mActivity == null || !mActivity.isChangingConfigurations()) { // Only stop the dream if the view is not detached by relaunching // activity for configuration changes. It is important to also clear @@ -1391,9 +1408,6 @@ public class DreamService extends Service implements Window.Callback { mActivity = null; finish(); } - if (mOverlayConnection != null && mDreamStartOverlayConsumer != null) { - mOverlayConnection.removeConsumer(mDreamStartOverlayConsumer); - } } }); } diff --git a/core/java/android/service/dreams/IDreamOverlay.aidl b/core/java/android/service/dreams/IDreamOverlay.aidl index 7aeceb2ce5380..0e4bd3bd547b6 100644 --- a/core/java/android/service/dreams/IDreamOverlay.aidl +++ b/core/java/android/service/dreams/IDreamOverlay.aidl @@ -41,4 +41,7 @@ interface IDreamOverlay { /** Called when the dream is waking, to do any exit animations */ void wakeUp(); + + /** Called when the dream has ended. */ + void endDream(); } diff --git a/core/java/com/android/internal/util/ObservableServiceConnection.java b/core/java/com/android/internal/util/ObservableServiceConnection.java index 3165d293bd91e..45256fd26cba6 100644 --- a/core/java/com/android/internal/util/ObservableServiceConnection.java +++ b/core/java/com/android/internal/util/ObservableServiceConnection.java @@ -164,6 +164,13 @@ public class ObservableServiceConnection implements ServiceConnection { mFlags = flags; } + /** + * Executes code on the executor specified at construction. + */ + public void execute(Runnable runnable) { + mExecutor.execute(runnable); + } + /** * Initiate binding to the service. * diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index a9a9caed314b1..dd01be0ef031d 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -208,6 +208,13 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ }); } + @Override + public void onEndDream() { + mExecutor.execute(() -> { + resetCurrentDreamOverlayLocked(); + }); + } + private Lifecycle.State getCurrentStateLocked() { return mLifecycleRegistry.getCurrentState(); } @@ -291,6 +298,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mDreamOverlayContainerViewController = null; mDreamOverlayTouchMonitor = null; + mWindow = null; mStarted = false; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index 4568d1e9b3bd8..3b8bb8089910f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -233,6 +233,32 @@ public class DreamOverlayServiceTest extends SysuiTestCase { verify(mStateController).setLowLightActive(true); } + @Test + public void testOnEndDream() throws RemoteException { + final IBinder proxy = mService.onBind(new Intent()); + final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); + + // Inform the overlay service of dream starting. + overlay.startDream(mWindowParams, mDreamOverlayCallback, + LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/); + mMainExecutor.runAllReady(); + + // Verify view added. + verify(mWindowManager).addView(mViewCaptor.capture(), any()); + + // Service destroyed. + mService.onEndDream(); + mMainExecutor.runAllReady(); + + // Verify view removed. + verify(mWindowManager).removeView(mViewCaptor.getValue()); + + // Verify state correctly set. + verify(mStateController).setOverlayActive(false); + verify(mStateController).setLowLightActive(false); + verify(mStateController).setEntryAnimationsFinished(false); + } + @Test public void testDestroy() throws RemoteException { final IBinder proxy = mService.onBind(new Intent());