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());