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
This commit is contained in:
Bryce Lee
2023-01-06 00:53:28 +00:00
parent 16a8a79644
commit 0aa0f4acb2
7 changed files with 82 additions and 9 deletions

View File

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

View File

@@ -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() {
}
/**

View File

@@ -297,14 +297,20 @@ public class DreamService extends Service implements Window.Callback {
}
public void addConsumer(Consumer<IDreamOverlay> consumer) {
mConsumers.add(consumer);
if (mOverlay != null) {
consumer.accept(mOverlay);
}
execute(() -> {
mConsumers.add(consumer);
if (mOverlay != null) {
consumer.accept(mOverlay);
}
});
}
public void removeConsumer(Consumer<IDreamOverlay> 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);
}
}
});
}

View File

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

View File

@@ -164,6 +164,13 @@ public class ObservableServiceConnection<T> 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.
*

View File

@@ -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;
}
}

View File

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