diff options
5 files changed, 178 insertions, 83 deletions
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java index 432444211bce..aa45c20a8e13 100644 --- a/core/java/android/service/dreams/DreamOverlayService.java +++ b/core/java/android/service/dreams/DreamOverlayService.java @@ -42,8 +42,11 @@ public abstract class DreamOverlayService extends Service { private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() { @Override public void startDream(WindowManager.LayoutParams layoutParams, - IDreamOverlayCallback callback) { + IDreamOverlayCallback callback, String dreamComponent, + boolean shouldShowComplications) { mDreamOverlayCallback = callback; + mDreamComponent = ComponentName.unflattenFromString(dreamComponent); + mShowComplications = shouldShowComplications; onStartDream(layoutParams); } }; @@ -56,10 +59,6 @@ public abstract class DreamOverlayService extends Service { @Nullable @Override public final IBinder onBind(@NonNull Intent intent) { - mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, - DreamService.DEFAULT_SHOW_COMPLICATIONS); - mDreamComponent = intent.getParcelableExtra(DreamService.EXTRA_DREAM_COMPONENT, - ComponentName.class); return mDreamOverlay.asBinder(); } diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 3b7698e3954b..3c1fef02f9ba 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -214,19 +214,6 @@ public class DreamService extends Service implements Window.Callback { private static final String DREAM_META_DATA_ROOT_TAG = "dream"; /** - * Extra containing a boolean for whether to show complications on the overlay. - * @hide - */ - public static final String EXTRA_SHOW_COMPLICATIONS = - "android.service.dreams.SHOW_COMPLICATIONS"; - - /** - * Extra containing the component name for the active dream. - * @hide - */ - public static final String EXTRA_DREAM_COMPONENT = "android.service.dreams.DREAM_COMPONENT"; - - /** * The default value for whether to show complications on the overlay. * * @hide @@ -252,6 +239,9 @@ public class DreamService extends Service implements Window.Callback { private boolean mDebug = false; + private ComponentName mDreamComponent; + private boolean mShouldShowComplications; + private DreamServiceWrapper mDreamServiceWrapper; private Runnable mDispatchAfterOnAttachedToWindow; @@ -947,6 +937,11 @@ public class DreamService extends Service implements Window.Callback { @Override public void onCreate() { if (mDebug) Slog.v(mTag, "onCreate()"); + + mDreamComponent = new ComponentName(this, getClass()); + mShouldShowComplications = fetchShouldShowComplications(this /*context*/, + fetchServiceInfo(this /*context*/, mDreamComponent)); + super.onCreate(); } @@ -994,14 +989,7 @@ public class DreamService extends Service implements Window.Callback { // Connect to the overlay service if present. if (!mWindowless && overlayComponent != null) { final Resources resources = getResources(); - final ComponentName dreamService = new ComponentName(this, getClass()); - - final ServiceInfo serviceInfo = fetchServiceInfo(this, dreamService); - final Intent overlayIntent = new Intent() - .setComponent(overlayComponent) - .putExtra(EXTRA_SHOW_COMPLICATIONS, - fetchShouldShowComplications(this, serviceInfo)) - .putExtra(EXTRA_DREAM_COMPONENT, dreamService); + final Intent overlayIntent = new Intent().setComponent(overlayComponent); mOverlayConnection = new OverlayConnection( /* context= */ this, @@ -1364,7 +1352,9 @@ public class DreamService extends Service implements Window.Callback { // parameters once the window has been attached. mDreamStartOverlayConsumer = overlay -> { try { - overlay.startDream(mWindow.getAttributes(), mOverlayCallback); + overlay.startDream(mWindow.getAttributes(), mOverlayCallback, + mDreamComponent.flattenToString(), + mShouldShowComplications); } catch (RemoteException e) { Log.e(mTag, "could not send window attributes:" + e); } diff --git a/core/java/android/service/dreams/IDreamOverlay.aidl b/core/java/android/service/dreams/IDreamOverlay.aidl index 2b6633d93dc5..05ebbfe98c9f 100644 --- a/core/java/android/service/dreams/IDreamOverlay.aidl +++ b/core/java/android/service/dreams/IDreamOverlay.aidl @@ -31,7 +31,11 @@ interface IDreamOverlay { * @param params The {@link LayoutParams} for the associated DreamWindow, including the window token of the Dream Activity. * @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the - * dream. + * dream. + * @param dreamComponent The component name of the dream service requesting overlay. + * @param shouldShowComplications Whether the dream overlay should show complications, e.g. clock + * and weather. */ - void startDream(in LayoutParams params, in IDreamOverlayCallback callback); + void startDream(in LayoutParams params, in IDreamOverlayCallback callback, + in String dreamComponent, in boolean shouldShowComplications); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index 696fc7254308..d1b73685a3f3 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -64,29 +64,26 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ private final Executor mExecutor; // A controller for the dream overlay container view (which contains both the status bar and the // content area). - private final DreamOverlayContainerViewController mDreamOverlayContainerViewController; + private DreamOverlayContainerViewController mDreamOverlayContainerViewController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Nullable private final ComponentName mLowLightDreamComponent; private final UiEventLogger mUiEventLogger; + private final WindowManager mWindowManager; // A reference to the {@link Window} used to hold the dream overlay. private Window mWindow; + // True if a dream has bound to the service and dream overlay service has started. + private boolean mStarted = false; + // True if the service has been destroyed. - private boolean mDestroyed; + private boolean mDestroyed = false; - private final Complication.Host mHost = new Complication.Host() { - @Override - public void requestExitDream() { - mExecutor.execute(DreamOverlayService.this::requestExit); - } - }; + private final DreamOverlayComponent mDreamOverlayComponent; private final LifecycleRegistry mLifecycleRegistry; - private ViewModelStore mViewModelStore = new ViewModelStore(); - private DreamOverlayTouchMonitor mDreamOverlayTouchMonitor; private final KeyguardUpdateMonitorCallback mKeyguardCallback = @@ -103,7 +100,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } }; - private DreamOverlayStateController mStateController; + private final DreamOverlayStateController mStateController; @VisibleForTesting public enum DreamOverlayEvent implements UiEventLogger.UiEventEnum { @@ -128,6 +125,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ public DreamOverlayService( Context context, @Main Executor executor, + WindowManager windowManager, DreamOverlayComponent.Factory dreamOverlayComponentFactory, DreamOverlayStateController stateController, KeyguardUpdateMonitor keyguardUpdateMonitor, @@ -136,19 +134,19 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ ComponentName lowLightDreamComponent) { mContext = context; mExecutor = executor; + mWindowManager = windowManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLowLightDreamComponent = lowLightDreamComponent; mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback); mStateController = stateController; mUiEventLogger = uiEventLogger; - final DreamOverlayComponent component = - dreamOverlayComponentFactory.create(mViewModelStore, mHost); - mDreamOverlayContainerViewController = component.getDreamOverlayContainerViewController(); + final ViewModelStore viewModelStore = new ViewModelStore(); + final Complication.Host host = + () -> mExecutor.execute(DreamOverlayService.this::requestExit); + mDreamOverlayComponent = dreamOverlayComponentFactory.create(viewModelStore, host); + mLifecycleRegistry = mDreamOverlayComponent.getLifecycleRegistry(); setCurrentState(Lifecycle.State.CREATED); - mLifecycleRegistry = component.getLifecycleRegistry(); - mDreamOverlayTouchMonitor = component.getDreamOverlayTouchMonitor(); - mDreamOverlayTouchMonitor.init(); } private void setCurrentState(Lifecycle.State state) { @@ -159,34 +157,48 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ public void onDestroy() { mKeyguardUpdateMonitor.removeCallback(mKeyguardCallback); setCurrentState(Lifecycle.State.DESTROYED); - final WindowManager windowManager = mContext.getSystemService(WindowManager.class); - if (mWindow != null) { - windowManager.removeView(mWindow.getDecorView()); - } - mStateController.setOverlayActive(false); - mStateController.setLowLightActive(false); + + resetCurrentDreamOverlay(); + mDestroyed = true; super.onDestroy(); } @Override public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) { - mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_ENTER_START); setCurrentState(Lifecycle.State.STARTED); - final ComponentName dreamComponent = getDreamComponent(); - mStateController.setLowLightActive( - dreamComponent != null && dreamComponent.equals(mLowLightDreamComponent)); + mExecutor.execute(() -> { + mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_ENTER_START); + if (mDestroyed) { // The task could still be executed after the service has been destroyed. Bail if // that is the case. return; } + + if (mStarted) { + // Reset the current dream overlay before starting a new one. This can happen + // when two dreams overlap (briefly, for a smoother dream transition) and both + // dreams are bound to the dream overlay service. + resetCurrentDreamOverlay(); + } + + mDreamOverlayContainerViewController = + mDreamOverlayComponent.getDreamOverlayContainerViewController(); + mDreamOverlayTouchMonitor = mDreamOverlayComponent.getDreamOverlayTouchMonitor(); + mDreamOverlayTouchMonitor.init(); + mStateController.setShouldShowComplications(shouldShowComplications()); addOverlayWindowLocked(layoutParams); setCurrentState(Lifecycle.State.RESUMED); mStateController.setOverlayActive(true); + final ComponentName dreamComponent = getDreamComponent(); + mStateController.setLowLightActive( + dreamComponent != null && dreamComponent.equals(mLowLightDreamComponent)); mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START); + + mStarted = true; }); } @@ -222,8 +234,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ removeContainerViewFromParent(); mWindow.setContentView(mDreamOverlayContainerViewController.getContainerView()); - final WindowManager windowManager = mContext.getSystemService(WindowManager.class); - windowManager.addView(mWindow.getDecorView(), mWindow.getAttributes()); + mWindowManager.addView(mWindow.getDecorView(), mWindow.getAttributes()); } private void removeContainerViewFromParent() { @@ -238,4 +249,18 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ Log.w(TAG, "Removing dream overlay container view parent!"); parentView.removeView(containerView); } + + private void resetCurrentDreamOverlay() { + if (mStarted && mWindow != null) { + mWindowManager.removeView(mWindow.getDecorView()); + } + + mStateController.setOverlayActive(false); + mStateController.setLowLightActive(false); + + mDreamOverlayContainerViewController = null; + mDreamOverlayTouchMonitor = 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 eec33ca2ff78..f370be190761 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -19,6 +19,7 @@ package com.android.systemui.dreams; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -27,10 +28,10 @@ import android.content.ComponentName; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; -import android.service.dreams.DreamService; import android.service.dreams.IDreamOverlay; import android.service.dreams.IDreamOverlayCallback; import android.testing.AndroidTestingRunner; +import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowManagerImpl; @@ -53,6 +54,8 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -61,6 +64,7 @@ import org.mockito.MockitoAnnotations; public class DreamOverlayServiceTest extends SysuiTestCase { private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package", "lowlight"); + private static final String DREAM_COMPONENT = "package/dream"; private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); @@ -108,12 +112,14 @@ public class DreamOverlayServiceTest extends SysuiTestCase { @Mock UiEventLogger mUiEventLogger; + @Captor + ArgumentCaptor<View> mViewCaptor; + DreamOverlayService mService; @Before public void setup() { MockitoAnnotations.initMocks(this); - mContext.addMockSystemService(WindowManager.class, mWindowManager); when(mDreamOverlayComponent.getDreamOverlayContainerViewController()) .thenReturn(mDreamOverlayContainerViewController); @@ -129,7 +135,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase { when(mDreamOverlayContainerViewController.getContainerView()) .thenReturn(mDreamOverlayContainerView); - mService = new DreamOverlayService(mContext, mMainExecutor, + mService = new DreamOverlayService(mContext, mMainExecutor, mWindowManager, mDreamOverlayComponentFactory, mStateController, mKeyguardUpdateMonitor, @@ -143,7 +149,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. - overlay.startDream(mWindowParams, mDreamOverlayCallback); + overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mUiEventLogger).log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START); @@ -157,7 +164,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. - overlay.startDream(mWindowParams, mDreamOverlayCallback); + overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mWindowManager).addView(any(), any()); @@ -169,7 +177,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. - overlay.startDream(mWindowParams, mDreamOverlayCallback); + overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mDreamOverlayContainerViewController).init(); @@ -186,49 +195,76 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. - overlay.startDream(mWindowParams, mDreamOverlayCallback); + overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView); } @Test - public void testShouldShowComplicationsFalseByDefault() { - mService.onBind(new Intent()); + public void testShouldShowComplicationsSetByStartDream() throws RemoteException { + final IBinder proxy = mService.onBind(new Intent()); + final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); - assertThat(mService.shouldShowComplications()).isFalse(); + // Inform the overlay service of dream starting. + overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + true /*shouldShowComplication*/); + + assertThat(mService.shouldShowComplications()).isTrue(); } @Test - public void testShouldShowComplicationsSetByIntentExtra() { - final Intent intent = new Intent(); - intent.putExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, true); - mService.onBind(intent); + public void testLowLightSetByStartDream() throws RemoteException { + final IBinder proxy = mService.onBind(new Intent()); + final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); - assertThat(mService.shouldShowComplications()).isTrue(); + // Inform the overlay service of dream starting. + overlay.startDream(mWindowParams, mDreamOverlayCallback, + LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/); + mMainExecutor.runAllReady(); + + assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT); + verify(mStateController).setLowLightActive(true); } @Test - public void testLowLightSetByIntentExtra() throws RemoteException { - final Intent intent = new Intent(); - intent.putExtra(DreamService.EXTRA_DREAM_COMPONENT, LOW_LIGHT_COMPONENT); - - final IBinder proxy = mService.onBind(intent); + public void testDestroy() throws RemoteException { + final IBinder proxy = mService.onBind(new Intent()); final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); - assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT); // Inform the overlay service of dream starting. - overlay.startDream(mWindowParams, mDreamOverlayCallback); + overlay.startDream(mWindowParams, mDreamOverlayCallback, + LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/); mMainExecutor.runAllReady(); - verify(mStateController).setLowLightActive(true); + // Verify view added. + verify(mWindowManager).addView(mViewCaptor.capture(), any()); + + // Service destroyed. + mService.onDestroy(); + mMainExecutor.runAllReady(); + + // Verify view removed. + verify(mWindowManager).removeView(mViewCaptor.getValue()); + + // Verify state correctly set. + verify(mKeyguardUpdateMonitor).removeCallback(any()); + verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED); + verify(mStateController).setOverlayActive(false); + verify(mStateController).setLowLightActive(false); } @Test - public void testDestroy() { + public void testDoNotRemoveViewOnDestroyIfOverlayNotStarted() { + // Service destroyed without ever starting dream. mService.onDestroy(); mMainExecutor.runAllReady(); + // Verify no view is removed. + verify(mWindowManager, never()).removeView(any()); + + // Verify state still correctly set. verify(mKeyguardUpdateMonitor).removeCallback(any()); verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED); verify(mStateController).setOverlayActive(false); @@ -245,7 +281,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. - overlay.startDream(mWindowParams, mDreamOverlayCallback); + overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*shouldShowComplication*/); // Destroy the service. mService.onDestroy(); @@ -255,4 +292,44 @@ public class DreamOverlayServiceTest extends SysuiTestCase { verify(mWindowManager, never()).addView(any(), any()); } + + @Test + public void testResetCurrentOverlayWhenConnectedToNewDream() throws RemoteException { + final IBinder proxy = mService.onBind(new Intent()); + final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); + + // Inform the overlay service of dream starting. Do not show dream complications. + overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*shouldShowComplication*/); + mMainExecutor.runAllReady(); + + // Verify that a new window is added. + verify(mWindowManager).addView(mViewCaptor.capture(), any()); + final View windowDecorView = mViewCaptor.getValue(); + + // Assert that the overlay is not showing complications. + assertThat(mService.shouldShowComplications()).isFalse(); + + clearInvocations(mDreamOverlayComponent); + clearInvocations(mWindowManager); + + // New dream starting with dream complications showing. Note that when a new dream is + // binding to the dream overlay service, it receives the same instance of IBinder as the + // first one. + overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + true /*shouldShowComplication*/); + mMainExecutor.runAllReady(); + + // Assert that the overlay is showing complications. + assertThat(mService.shouldShowComplications()).isTrue(); + + // Verify that the old overlay window has been removed, and a new one created. + verify(mWindowManager).removeView(windowDecorView); + verify(mWindowManager).addView(any(), any()); + + // Verify that new instances of overlay container view controller and overlay touch monitor + // are created. + verify(mDreamOverlayComponent).getDreamOverlayContainerViewController(); + verify(mDreamOverlayComponent).getDreamOverlayTouchMonitor(); + } } |