Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1de9f064 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

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
parent 99debb96
Loading
Loading
Loading
Loading
+75 −1
Original line number Diff line number Diff line
@@ -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<Presentation> 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
+16 −1
Original line number Diff line number Diff line
@@ -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));
    }
}