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

Commit 6b51ff20 authored by Kevin Chyn's avatar Kevin Chyn Committed by Automerger Merge Worker
Browse files

Merge changes from topic "concurrent-keyguard" into udc-dev am: cc5495e9

parents 1f664c79 cc5495e9
Loading
Loading
Loading
Loading
+16 −5
Original line number Original line Diff line number Diff line
@@ -342,11 +342,22 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
                            mRearDisplayPresentationController);
                            mRearDisplayPresentationController);
            DeviceStateRequest concurrentDisplayStateRequest = DeviceStateRequest.newBuilder(
            DeviceStateRequest concurrentDisplayStateRequest = DeviceStateRequest.newBuilder(
                    mConcurrentDisplayState).build();
                    mConcurrentDisplayState).build();

            try {
                mDeviceStateManager.requestState(
                mDeviceStateManager.requestState(
                        concurrentDisplayStateRequest,
                        concurrentDisplayStateRequest,
                        mExecutor,
                        mExecutor,
                        deviceStateCallback
                        deviceStateCallback
                );
                );
            } catch (SecurityException e) {
                // If a SecurityException occurs when invoking DeviceStateManager#requestState
                // (e.g. if the caller is not in the foreground, or if it does not have the required
                // permissions), we should first clean up our local state before re-throwing the
                // SecurityException to the caller. Otherwise, subsequent attempts to
                // startRearDisplayPresentationSession will always fail.
                mRearDisplayPresentationController = null;
                throw e;
            }
        }
        }
    }
    }


+75 −1
Original line number Original line Diff line number Diff line
@@ -19,27 +19,34 @@ import android.app.Presentation;
import android.content.Context;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager;
import android.media.MediaRouter;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.media.MediaRouter.RouteInfo;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Trace;
import android.os.Trace;
import android.text.TextUtils;
import android.util.Log;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseArray;
import android.view.Display;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayInfo;
import android.view.DisplayInfo;
import android.view.LayoutInflater;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager;


import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.R;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;


import java.util.concurrent.Executor;
import java.util.concurrent.Executor;


@@ -47,6 +54,7 @@ import javax.inject.Inject;


import dagger.Lazy;
import dagger.Lazy;


@SysUISingleton
public class KeyguardDisplayManager {
public class KeyguardDisplayManager {
    protected static final String TAG = "KeyguardDisplayManager";
    protected static final String TAG = "KeyguardDisplayManager";
    private static final boolean DEBUG = KeyguardConstants.DEBUG;
    private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -61,6 +69,9 @@ public class KeyguardDisplayManager {
    private boolean mShowing;
    private boolean mShowing;
    private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
    private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();


    private final DeviceStateHelper mDeviceStateHelper;
    private final KeyguardStateController mKeyguardStateController;

    private final SparseArray<Presentation> mPresentations = new SparseArray<>();
    private final SparseArray<Presentation> mPresentations = new SparseArray<>();


    private final DisplayTracker.Callback mDisplayCallback =
    private final DisplayTracker.Callback mDisplayCallback =
@@ -92,7 +103,9 @@ public class KeyguardDisplayManager {
            KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
            KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
            DisplayTracker displayTracker,
            DisplayTracker displayTracker,
            @Main Executor mainExecutor,
            @Main Executor mainExecutor,
            @UiBackground Executor uiBgExecutor) {
            @UiBackground Executor uiBgExecutor,
            DeviceStateHelper deviceStateHelper,
            KeyguardStateController keyguardStateController) {
        mContext = context;
        mContext = context;
        mNavigationBarControllerLazy = navigationBarControllerLazy;
        mNavigationBarControllerLazy = navigationBarControllerLazy;
        mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
        mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
@@ -100,6 +113,8 @@ public class KeyguardDisplayManager {
        mDisplayService = mContext.getSystemService(DisplayManager.class);
        mDisplayService = mContext.getSystemService(DisplayManager.class);
        mDisplayTracker = displayTracker;
        mDisplayTracker = displayTracker;
        mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
        mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
        mDeviceStateHelper = deviceStateHelper;
        mKeyguardStateController = keyguardStateController;
    }
    }


    private boolean isKeyguardShowable(Display display) {
    private boolean isKeyguardShowable(Display display) {
@@ -122,6 +137,18 @@ public class KeyguardDisplayManager {
            }
            }
            return false;
            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;
        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
    @VisibleForTesting
    static final class KeyguardPresentation extends Presentation {
    static final class KeyguardPresentation extends Presentation {
        private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
        private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
+16 −1
Original line number Original line 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.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;


import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerGlobal;
import android.testing.AndroidTestingRunner;
import android.testing.AndroidTestingRunner;
@@ -38,6 +39,7 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;


import org.junit.Before;
import org.junit.Before;
import org.junit.Test;
import org.junit.Test;
@@ -58,6 +60,10 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
    private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
    private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
    @Mock
    @Mock
    private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation;
    private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation;
    @Mock
    private KeyguardDisplayManager.DeviceStateHelper mDeviceStateHelper;
    @Mock
    private KeyguardStateController mKeyguardStateController;


    private Executor mMainExecutor = Runnable::run;
    private Executor mMainExecutor = Runnable::run;
    private Executor mBackgroundExecutor = Runnable::run;
    private Executor mBackgroundExecutor = Runnable::run;
@@ -76,7 +82,7 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
        MockitoAnnotations.initMocks(this);
        MockitoAnnotations.initMocks(this);
        mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController,
        mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController,
                mKeyguardStatusViewComponentFactory, mDisplayTracker, mMainExecutor,
                mKeyguardStatusViewComponentFactory, mDisplayTracker, mMainExecutor,
                mBackgroundExecutor));
                mBackgroundExecutor, mDeviceStateHelper, mKeyguardStateController));
        doReturn(mKeyguardPresentation).when(mManager).createPresentation(any());
        doReturn(mKeyguardPresentation).when(mManager).createPresentation(any());


        mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
        mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
@@ -123,4 +129,13 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
        mManager.show();
        mManager.show();
        verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay));
        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));
    }
}
}
+46 −8
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.devicestate;
package com.android.server.devicestate;


import static android.Manifest.permission.CONTROL_DEVICE_STATE;
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
@@ -73,6 +74,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.HashSet;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Optional;
import java.util.Set;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.WeakHashMap;
@@ -162,7 +164,7 @@ public final class DeviceStateManagerService extends SystemService {
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
    private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();


    private Set<Integer> mDeviceStatesAvailableForAppRequests;
    private Set<Integer> mDeviceStatesAvailableForAppRequests = new HashSet<>();


    private Set<Integer> mFoldedDeviceStates;
    private Set<Integer> mFoldedDeviceStates;


@@ -879,8 +881,16 @@ public final class DeviceStateManagerService extends SystemService {
     * @param callingPid Process ID that is requesting this state change
     * @param callingPid Process ID that is requesting this state change
     * @param state state that is being requested.
     * @param state state that is being requested.
     */
     */
    private void assertCanRequestDeviceState(int callingPid, int state) {
    private void assertCanRequestDeviceState(int callingPid, int callingUid, int state) {
        if (!isTopApp(callingPid) || !isStateAvailableForAppRequests(state)) {
        final boolean isTopApp = isTopApp(callingPid);
        final boolean isForegroundApp = isForegroundApp(callingPid, callingUid);
        final boolean isStateAvailableForAppRequests = isStateAvailableForAppRequests(state);

        final boolean canRequestState = isTopApp
                && isForegroundApp
                && isStateAvailableForAppRequests;

        if (!canRequestState) {
            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
                    "Permission required to request device state, "
                    "Permission required to request device state, "
                            + "or the call must come from the top app "
                            + "or the call must come from the top app "
@@ -893,15 +903,43 @@ public final class DeviceStateManagerService extends SystemService {
     * not the top app, then check if this process holds the CONTROL_DEVICE_STATE permission.
     * not the top app, then check if this process holds the CONTROL_DEVICE_STATE permission.
     *
     *
     * @param callingPid Process ID that is requesting this state change
     * @param callingPid Process ID that is requesting this state change
     * @param callingUid UID that is requesting this state change
     */
     */
    private void assertCanControlDeviceState(int callingPid) {
    private void assertCanControlDeviceState(int callingPid, int callingUid) {
        if (!isTopApp(callingPid)) {
        final boolean isTopApp = isTopApp(callingPid);
        final boolean isForegroundApp = isForegroundApp(callingPid, callingUid);

        final boolean canControlState = isTopApp && isForegroundApp;

        if (!canControlState) {
            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
                    "Permission required to request device state, "
                    "Permission required to request device state, "
                            + "or the call must come from the top app.");
                            + "or the call must come from the top app.");
        }
        }
    }
    }


    /**
     * Checks if the caller is in the foreground. Note that callers may be the top app as returned
     * from {@link #isTopApp(int)}, but not be in the foreground. For example, keyguard may be on
     * top of the top app.
     */
    private boolean isForegroundApp(int callingPid, int callingUid) {
        try {
            final List<ActivityManager.RunningAppProcessInfo> procs =
                    ActivityManager.getService().getRunningAppProcesses();
            for (int i = 0; i < procs.size(); i++) {
                ActivityManager.RunningAppProcessInfo proc = procs.get(i);
                if (proc.pid == callingPid && proc.uid == callingUid
                        && proc.importance <= IMPORTANCE_FOREGROUND) {
                    return true;
                }
            }
        } catch (RemoteException e) {
            Slog.w(TAG, "am.getRunningAppProcesses() failed", e);
        }
        return false;
    }

    private boolean isTopApp(int callingPid) {
    private boolean isTopApp(int callingPid) {
        final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp();
        final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp();
        return topApp != null && topApp.getPid() == callingPid;
        return topApp != null && topApp.getPid() == callingPid;
@@ -918,7 +956,6 @@ public final class DeviceStateManagerService extends SystemService {
     */
     */
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private void readStatesAvailableForRequestFromApps() {
    private void readStatesAvailableForRequestFromApps() {
        mDeviceStatesAvailableForAppRequests = new HashSet<>();
        String[] availableAppStatesConfigIdentifiers = getContext().getResources()
        String[] availableAppStatesConfigIdentifiers = getContext().getResources()
                .getStringArray(R.array.config_deviceStatesAvailableForAppRequests);
                .getStringArray(R.array.config_deviceStatesAvailableForAppRequests);
        for (int i = 0; i < availableAppStatesConfigIdentifiers.length; i++) {
        for (int i = 0; i < availableAppStatesConfigIdentifiers.length; i++) {
@@ -1118,7 +1155,7 @@ public final class DeviceStateManagerService extends SystemService {
            // Allow top processes to request a device state change
            // Allow top processes to request a device state change
            // If the calling process ID is not the top app, then we check if this process
            // If the calling process ID is not the top app, then we check if this process
            // holds a permission to CONTROL_DEVICE_STATE
            // holds a permission to CONTROL_DEVICE_STATE
            assertCanRequestDeviceState(callingPid, state);
            assertCanRequestDeviceState(callingPid, callingUid, state);


            if (token == null) {
            if (token == null) {
                throw new IllegalArgumentException("Request token must not be null.");
                throw new IllegalArgumentException("Request token must not be null.");
@@ -1139,10 +1176,11 @@ public final class DeviceStateManagerService extends SystemService {
        @Override // Binder call
        @Override // Binder call
        public void cancelStateRequest() {
        public void cancelStateRequest() {
            final int callingPid = Binder.getCallingPid();
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            // Allow top processes to cancel a device state change
            // Allow top processes to cancel a device state change
            // If the calling process ID is not the top app, then we check if this process
            // If the calling process ID is not the top app, then we check if this process
            // holds a permission to CONTROL_DEVICE_STATE
            // holds a permission to CONTROL_DEVICE_STATE
            assertCanControlDeviceState(callingPid);
            assertCanControlDeviceState(callingPid, callingUid);


            final long callingIdentity = Binder.clearCallingIdentity();
            final long callingIdentity = Binder.clearCallingIdentity();
            try {
            try {