From c45e60c5ad7206585eef3fcbe0f60e481d5f8747 Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Wed, 5 Apr 2023 22:06:13 +0000 Subject: [PATCH 1/2] Send dream state updates through DreamManagerStateListener. This changelist extends DreamManagerStateListener to also send updates about the dream state, including when the dream started and stopped. This allows system server components to receive state updates without needed to listen to the dream state broadcasts. Test: atest DreamControllerTest#startDream_dreamListenerNotified Test: atest DreamControllerTest#stopDream_dreamListenerNotified Bug: 275108597 Change-Id: Ifb1e1266b938d50e0744aa106557c8c7db52f442 --- .../service/dreams/DreamManagerInternal.java | 15 ++++++- .../server/dreams/DreamController.java | 2 + .../server/dreams/DreamManagerService.java | 28 ++++++++++++- .../server/policy/PhoneWindowManager.java | 21 ---------- .../keyguard/KeyguardServiceDelegate.java | 20 ++++++++++ .../server/dreams/DreamControllerTest.java | 40 +++++++++++++++++++ 6 files changed, 103 insertions(+), 23 deletions(-) diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java index 82571db469be6..e9bb28c252e02 100644 --- a/core/java/android/service/dreams/DreamManagerInternal.java +++ b/core/java/android/service/dreams/DreamManagerInternal.java @@ -84,6 +84,19 @@ public abstract class DreamManagerInternal { * * @param keepDreaming True if the current dream should continue when undocking. */ - void onKeepDreamingWhenUnpluggingChanged(boolean keepDreaming); + default void onKeepDreamingWhenUnpluggingChanged(boolean keepDreaming) { + } + + /** + * Called when dreaming has started. + */ + default void onDreamingStarted() { + } + + /** + * Called when dreaming has stopped. + */ + default void onDreamingStopped() { + } } } diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index de10b1b19a334..6d70d21e3b84e 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -345,6 +345,7 @@ final class DreamController { if (!mCurrentDream.mIsPreviewMode && !mSentStartBroadcast) { mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL, null /* receiverPermission */, mDreamingStartedStoppedOptions); + mListener.onDreamStarted(mCurrentDream.mToken); mSentStartBroadcast = true; } } @@ -353,6 +354,7 @@ final class DreamController { * Callback interface to be implemented by the {@link DreamManagerService}. */ public interface Listener { + void onDreamStarted(Binder token); void onDreamStopped(Binder token); } diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 0e26d46610172..d2dcc508d01f8 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -84,6 +84,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Consumer; /** * Service api for managing dreams. @@ -341,10 +342,24 @@ public final class DreamManagerService extends SystemService { } private void reportKeepDreamingWhenUnpluggingChanged(boolean keepDreaming) { + notifyDreamStateListeners( + listener -> listener.onKeepDreamingWhenUnpluggingChanged(keepDreaming)); + } + + private void reportDreamingStarted() { + notifyDreamStateListeners(listener -> listener.onDreamingStarted()); + } + + private void reportDreamingStopped() { + notifyDreamStateListeners(listener -> listener.onDreamingStopped()); + } + + private void notifyDreamStateListeners( + Consumer notifier) { mHandler.post(() -> { for (DreamManagerInternal.DreamManagerStateListener listener : mDreamManagerStateListeners) { - listener.onKeepDreamingWhenUnpluggingChanged(keepDreaming); + notifier.accept(listener); } }); } @@ -766,6 +781,15 @@ public final class DreamManagerService extends SystemService { } private final DreamController.Listener mControllerListener = new DreamController.Listener() { + @Override + public void onDreamStarted(Binder token) { + // Note that this event is distinct from DreamManagerService#startDreamLocked as it + // tracks the DreamService attach point from DreamController, closest to the broadcast + // of ACTION_DREAMING_STARTED. + + reportDreamingStarted(); + } + @Override public void onDreamStopped(Binder token) { synchronized (mLock) { @@ -773,6 +797,8 @@ public final class DreamManagerService extends SystemService { cleanupDreamLocked(); } } + + reportDreamingStopped(); } }; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index fc6b4e9dcb20d..38c2be83f070e 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2186,12 +2186,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { Intent.EXTRA_DOCK_STATE_UNDOCKED)); } - // register for dream-related broadcasts - filter = new IntentFilter(); - filter.addAction(Intent.ACTION_DREAMING_STARTED); - filter.addAction(Intent.ACTION_DREAMING_STOPPED); - mContext.registerReceiver(mDreamReceiver, filter); - // register for multiuser-relevant broadcasts filter = new IntentFilter(Intent.ACTION_USER_SWITCHED); mContext.registerReceiver(mMultiuserReceiver, filter); @@ -4774,21 +4768,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; - BroadcastReceiver mDreamReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_DREAMING_STARTED.equals(intent.getAction())) { - if (mKeyguardDelegate != null) { - mKeyguardDelegate.onDreamingStarted(); - } - } else if (Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())) { - if (mKeyguardDelegate != null) { - mKeyguardDelegate.onDreamingStopped(); - } - } - } - }; - BroadcastReceiver mMultiuserReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java index 646dc4e98c396..495e239d4cd76 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java @@ -18,6 +18,7 @@ import android.os.IBinder; import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; +import android.service.dreams.DreamManagerInternal; import android.util.Log; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -27,6 +28,7 @@ import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IKeyguardDrawnCallback; import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardService; +import com.android.server.LocalServices; import com.android.server.UiThread; import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult; import com.android.server.wm.EventLogTags; @@ -60,6 +62,19 @@ public class KeyguardServiceDelegate { private DrawnListener mDrawnListenerWhenConnect; + private final DreamManagerInternal.DreamManagerStateListener mDreamManagerStateListener = + new DreamManagerInternal.DreamManagerStateListener() { + @Override + public void onDreamingStarted() { + KeyguardServiceDelegate.this.onDreamingStarted(); + } + + @Override + public void onDreamingStopped() { + KeyguardServiceDelegate.this.onDreamingStopped(); + } + }; + private static final class KeyguardState { KeyguardState() { reset(); @@ -158,6 +173,11 @@ public class KeyguardServiceDelegate { } else { if (DEBUG) Log.v(TAG, "*** Keyguard started"); } + + final DreamManagerInternal dreamManager = + LocalServices.getService(DreamManagerInternal.class); + + dreamManager.registerDreamManagerStateListener(mDreamManagerStateListener); } private final ServiceConnection mKeyguardConnection = new ServiceConnection() { diff --git a/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java b/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java index 1ef11974292b2..d5ad815d3cdb9 100644 --- a/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.ActivityTaskManager; import android.content.ComponentName; import android.content.Context; import android.content.ServiceConnection; @@ -54,6 +55,10 @@ public class DreamControllerTest { private DreamController.Listener mListener; @Mock private Context mContext; + + @Mock + private ActivityTaskManager mActivityTaskManager; + @Mock private IBinder mIBinder; @Mock @@ -80,6 +85,10 @@ public class DreamControllerTest { when(mIDreamService.asBinder()).thenReturn(mIBinder); when(mIBinder.queryLocalInterface(anyString())).thenReturn(mIDreamService); when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true); + when(mContext.getSystemService(Context.ACTIVITY_TASK_SERVICE)) + .thenReturn(mActivityTaskManager); + when(mContext.getSystemServiceName(ActivityTaskManager.class)) + .thenReturn(Context.ACTIVITY_TASK_SERVICE); mToken = new Binder(); mDreamName = ComponentName.unflattenFromString("dream"); @@ -103,6 +112,37 @@ public class DreamControllerTest { eq(false) /*preview*/, any()); } + @Test + public void startDream_dreamListenerNotified() { + // Call dream controller to start dreaming. + mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/, + 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/); + + // Mock service connected. + final ServiceConnection serviceConnection = captureServiceConnection(); + serviceConnection.onServiceConnected(mDreamName, mIBinder); + mLooper.dispatchAll(); + + // Verify that dream service is called to attach. + verify(mListener).onDreamStarted(any()); + } + + @Test + public void stopDream_dreamListenerNotified() { + // Start dream. + mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/, + 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/); + captureServiceConnection().onServiceConnected(mDreamName, mIBinder); + mLooper.dispatchAll(); + + // Stop dream. + mDreamController.stopDream(true /*immediate*/, "test stop dream" /*reason*/); + mLooper.dispatchAll(); + + // Verify that dream service is called to detach. + verify(mListener).onDreamStopped(any()); + } + @Test public void startDream_attachOnServiceConnectedInPreviewMode() throws RemoteException { // Call dream controller to start dreaming. From 1ff5b3e7cfa2c3fc3e08c869be11493c931f17d1 Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Sat, 1 Apr 2023 01:57:21 +0000 Subject: [PATCH 2/2] Use KeyguardUpdateMonitor in DreamCondition. This changelist moves DreamCondition away from using dream broadcasts for dream state updates to instead KeyguardUpdateMonitor. This move coincides with updates to improve the latency of dream state updates to KeyguardUpdateMonitor. Test: atest DreamConditionTest Fixes: 275108597 Change-Id: Idd362f882a822d3e670eb08e424a5ac145cb9479 --- .../dreams/conditions/DreamCondition.java | 50 ++++++------------- .../dreams/conditions/DreamConditionTest.java | 28 +++++++---- 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java index 5bbfbda829441..3ef19b760826a 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java @@ -16,12 +16,9 @@ package com.android.systemui.dreams.conditions; import android.app.DreamManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.text.TextUtils; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.shared.condition.Condition; import javax.inject.Inject; @@ -30,48 +27,33 @@ import javax.inject.Inject; * {@link DreamCondition} provides a signal when a dream begins and ends. */ public class DreamCondition extends Condition { - private final Context mContext; private final DreamManager mDreamManager; - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - processIntent(intent); - } - }; + private final KeyguardUpdateMonitor mUpdateMonitor; + + + private final KeyguardUpdateMonitorCallback mUpdateCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onDreamingStateChanged(boolean dreaming) { + updateCondition(dreaming); + } + }; @Inject - public DreamCondition(Context context, - DreamManager dreamManager) { - mContext = context; + public DreamCondition(DreamManager dreamManager, KeyguardUpdateMonitor monitor) { mDreamManager = dreamManager; - } - - private void processIntent(Intent intent) { - // In the case of a non-existent sticky broadcast, ignore when there is no intent. - if (intent == null) { - return; - } - if (TextUtils.equals(intent.getAction(), Intent.ACTION_DREAMING_STARTED)) { - updateCondition(true); - } else if (TextUtils.equals(intent.getAction(), Intent.ACTION_DREAMING_STOPPED)) { - updateCondition(false); - } else { - throw new IllegalStateException("unexpected intent:" + intent); - } + mUpdateMonitor = monitor; } @Override protected void start() { - final IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_DREAMING_STARTED); - filter.addAction(Intent.ACTION_DREAMING_STOPPED); - mContext.registerReceiver(mReceiver, filter); + mUpdateMonitor.registerCallback(mUpdateCallback); updateCondition(mDreamManager.isDreaming()); } @Override protected void stop() { - mContext.unregisterReceiver(mReceiver); + mUpdateMonitor.removeCallback(mUpdateCallback); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java index 58eb7d4f3ea76..e1c54976d7343 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java @@ -18,7 +18,6 @@ package com.android.systemui.dreams.conditions; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; @@ -26,13 +25,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.DreamManager; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.SysuiTestCase; import com.android.systemui.shared.condition.Condition; @@ -55,6 +54,9 @@ public class DreamConditionTest extends SysuiTestCase { @Mock DreamManager mDreamManager; + @Mock + KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -66,7 +68,7 @@ public class DreamConditionTest extends SysuiTestCase { @Test public void testInitialDreamingState() { when(mDreamManager.isDreaming()).thenReturn(true); - final DreamCondition condition = new DreamCondition(mContext, mDreamManager); + final DreamCondition condition = new DreamCondition(mDreamManager, mKeyguardUpdateMonitor); condition.addCallback(mCallback); verify(mCallback).onConditionChanged(eq(condition)); @@ -79,7 +81,7 @@ public class DreamConditionTest extends SysuiTestCase { @Test public void testInitialNonDreamingState() { when(mDreamManager.isDreaming()).thenReturn(false); - final DreamCondition condition = new DreamCondition(mContext, mDreamManager); + final DreamCondition condition = new DreamCondition(mDreamManager, mKeyguardUpdateMonitor); condition.addCallback(mCallback); verify(mCallback, never()).onConditionChanged(eq(condition)); @@ -91,15 +93,21 @@ public class DreamConditionTest extends SysuiTestCase { */ @Test public void testChange() { - final ArgumentCaptor receiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); + final ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); when(mDreamManager.isDreaming()).thenReturn(true); - final DreamCondition condition = new DreamCondition(mContext, mDreamManager); + final DreamCondition condition = new DreamCondition(mDreamManager, mKeyguardUpdateMonitor); condition.addCallback(mCallback); - verify(mContext).registerReceiver(receiverCaptor.capture(), any()); + verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture()); + clearInvocations(mCallback); - receiverCaptor.getValue().onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STOPPED)); + callbackCaptor.getValue().onDreamingStateChanged(false); verify(mCallback).onConditionChanged(eq(condition)); assertThat(condition.isConditionMet()).isFalse(); + + clearInvocations(mCallback); + callbackCaptor.getValue().onDreamingStateChanged(true); + verify(mCallback).onConditionChanged(eq(condition)); + assertThat(condition.isConditionMet()).isTrue(); } }