From 1de9f064562e47323fee8fc1537e0f419bd48498 Mon Sep 17 00:00:00 2001 From: Kevin Chyn Date: Tue, 2 May 2023 19:08:02 +0000 Subject: [PATCH] Do not show Keyguard Presentation when occluded ..., in concurrent mode, and if it is the display being used for concurrent mode. When windows with FLAG_SHOW_WHEN_LOCKED are showing on top of keyguard, the keyguard state becomes "occluded". When this happens, we should not show the Keyguard Presentation on the display that's being used for concurrent mode. This fix allows activities with FLAG_SHOW_WHEN_LOCKED to present content to the non-default display when keyguard is locked. Fixes: 270671994 Bug: 271317597 Test: manual (b/270671994 comment#10) Test: atest KeyguardDisplayManagerTest Change-Id: I79235fdd42ee23f2f4178397937eec859c56d844 --- .../keyguard/KeyguardDisplayManager.java | 76 ++++++++++++++++++- .../keyguard/KeyguardDisplayManagerTest.java | 17 ++++- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index ec8fa921c6fab..9f21a31bd2c05 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -19,27 +19,34 @@ import android.app.Presentation; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; +import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.DisplayManager; import android.media.MediaRouter; import android.media.MediaRouter.RouteInfo; import android.os.Bundle; import android.os.Trace; +import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import android.view.Display; +import android.view.DisplayAddress; import android.view.DisplayInfo; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; +import androidx.annotation.Nullable; + import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.settings.DisplayTracker; +import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.concurrent.Executor; @@ -47,6 +54,7 @@ import javax.inject.Inject; import dagger.Lazy; +@SysUISingleton public class KeyguardDisplayManager { protected static final String TAG = "KeyguardDisplayManager"; private static final boolean DEBUG = KeyguardConstants.DEBUG; @@ -61,6 +69,9 @@ public class KeyguardDisplayManager { private boolean mShowing; private final DisplayInfo mTmpDisplayInfo = new DisplayInfo(); + private final DeviceStateHelper mDeviceStateHelper; + private final KeyguardStateController mKeyguardStateController; + private final SparseArray mPresentations = new SparseArray<>(); private final DisplayTracker.Callback mDisplayCallback = @@ -92,7 +103,9 @@ public class KeyguardDisplayManager { KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, DisplayTracker displayTracker, @Main Executor mainExecutor, - @UiBackground Executor uiBgExecutor) { + @UiBackground Executor uiBgExecutor, + DeviceStateHelper deviceStateHelper, + KeyguardStateController keyguardStateController) { mContext = context; mNavigationBarControllerLazy = navigationBarControllerLazy; mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; @@ -100,6 +113,8 @@ public class KeyguardDisplayManager { mDisplayService = mContext.getSystemService(DisplayManager.class); mDisplayTracker = displayTracker; mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor); + mDeviceStateHelper = deviceStateHelper; + mKeyguardStateController = keyguardStateController; } private boolean isKeyguardShowable(Display display) { @@ -122,6 +137,18 @@ public class KeyguardDisplayManager { } return false; } + if (mKeyguardStateController.isOccluded() + && mDeviceStateHelper.isConcurrentDisplayActive(display)) { + if (DEBUG) { + // When activities with FLAG_SHOW_WHEN_LOCKED are shown on top of Keyguard, the + // Keyguard state becomes "occluded". In this case, we should not show the + // KeyguardPresentation, since the activity is presenting content onto the + // non-default display. + Log.i(TAG, "Do not show KeyguardPresentation when occluded and concurrent" + + " display is active"); + } + return false; + } return true; } @@ -260,6 +287,53 @@ public class KeyguardDisplayManager { } + /** + * Helper used to receive device state info from {@link DeviceStateManager}. + */ + static class DeviceStateHelper implements DeviceStateManager.DeviceStateCallback { + + @Nullable + private final DisplayAddress.Physical mRearDisplayPhysicalAddress; + + // TODO(b/271317597): These device states should be defined in DeviceStateManager + private final int mConcurrentState; + private boolean mIsInConcurrentDisplayState; + + @Inject + DeviceStateHelper(Context context, + DeviceStateManager deviceStateManager, + @Main Executor mainExecutor) { + + final String rearDisplayPhysicalAddress = context.getResources().getString( + com.android.internal.R.string.config_rearDisplayPhysicalAddress); + if (TextUtils.isEmpty(rearDisplayPhysicalAddress)) { + mRearDisplayPhysicalAddress = null; + } else { + mRearDisplayPhysicalAddress = DisplayAddress + .fromPhysicalDisplayId(Long.parseLong(rearDisplayPhysicalAddress)); + } + + mConcurrentState = context.getResources().getInteger( + com.android.internal.R.integer.config_deviceStateConcurrentRearDisplay); + deviceStateManager.registerCallback(mainExecutor, this); + } + + @Override + public void onStateChanged(int state) { + // When concurrent state ends, the display also turns off. This is enforced in various + // ExtensionRearDisplayPresentationTest CTS tests. So, we don't need to invoke + // hide() since that will happen through the onDisplayRemoved callback. + mIsInConcurrentDisplayState = state == mConcurrentState; + } + + boolean isConcurrentDisplayActive(Display display) { + return mIsInConcurrentDisplayState + && mRearDisplayPhysicalAddress != null + && mRearDisplayPhysicalAddress.equals(display.getAddress()); + } + } + + @VisibleForTesting static final class KeyguardPresentation extends Presentation { private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java index 30e3d09299f2c..b3496967f5256 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.hardware.display.DisplayManagerGlobal; import android.testing.AndroidTestingRunner; @@ -38,6 +39,7 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.SysuiTestCase; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.settings.FakeDisplayTracker; +import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; import org.junit.Test; @@ -58,6 +60,10 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase { private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; @Mock private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation; + @Mock + private KeyguardDisplayManager.DeviceStateHelper mDeviceStateHelper; + @Mock + private KeyguardStateController mKeyguardStateController; private Executor mMainExecutor = Runnable::run; private Executor mBackgroundExecutor = Runnable::run; @@ -76,7 +82,7 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController, mKeyguardStatusViewComponentFactory, mDisplayTracker, mMainExecutor, - mBackgroundExecutor)); + mBackgroundExecutor, mDeviceStateHelper, mKeyguardStateController)); doReturn(mKeyguardPresentation).when(mManager).createPresentation(any()); mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY, @@ -123,4 +129,13 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase { mManager.show(); verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay)); } + + @Test + public void testShow_concurrentDisplayActive_occluded() { + mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay}); + + when(mDeviceStateHelper.isConcurrentDisplayActive(mSecondaryDisplay)).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); + verify(mManager, never()).createPresentation(eq(mSecondaryDisplay)); + } }