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

Commit 4aeac6c7 authored by Fiona Campbell's avatar Fiona Campbell Committed by Android (Google) Code Review
Browse files

Merge "Add connected displays to appropriate power group" into main

parents 6aae325a b2bbb9f5
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -32,6 +32,8 @@ public class DisplayGroup {
    private final List<LogicalDisplay> mDisplays = new ArrayList<>();
    private final int mGroupId;

    private String mGroupName;

    private int mChangeCount;

    DisplayGroup(int groupId) {
@@ -43,6 +45,14 @@ public class DisplayGroup {
        return mGroupId;
    }

    public String getGroupName() {
        return mGroupName;
    }

    public void setGroupName(String groupName) {
        mGroupName = groupName;
    }

    /**
     * Adds the provided {@code display} to the Group
     *
@@ -107,6 +117,7 @@ public class DisplayGroup {
            LogicalDisplay logicalDisplay = mDisplays.get(i);
            ipw.println("Display " + logicalDisplay.getDisplayIdLocked() + " "
                    + logicalDisplay.getPrimaryDisplayDeviceLocked());
            ipw.println("Group name: " + getGroupName());
        }
    }
}
+165 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.display;

import android.content.Context;
import android.util.SparseArray;
import android.view.Display;

import com.android.server.wm.DesktopModeHelper;

/**
 * DisplayGroupAllocator helps select which display groups new displays should be added to,
 * when they are added to the system. It takes into account attributes of the display, as well
 * as which display groups already exist.
 */
class DisplayGroupAllocator {
    private static final String TAG = "DisplayGroupAllocator";

    private boolean mCanDeviceEnterDesktopMode;
    private boolean mCanDefaultDisplayEnterDesktopMode;
    private Context mContext;
    private final Injector mInjector;
    private int mReason;

    public static final String GROUP_TYPE_PRIMARY = "";
    public static final String GROUP_TYPE_SECONDARY = "secondary_mode";
    public static final int REASON_NON_DESKTOP = 0;
    public static final int REASON_PROJECTED = 1;
    public static final int REASON_EXTENDED = 2;
    public static final int REASON_FALLBACK = 3;


    DisplayGroupAllocator(Context context) {
        this(context, new Injector());
    }

    DisplayGroupAllocator(Context context, Injector injector) {
        mInjector = injector;
        mContext = context;
    }

    public void initLater(Context context) {
        mContext = context;
        mCanDeviceEnterDesktopMode = mInjector.canEnterDesktopMode(mContext);
        mCanDefaultDisplayEnterDesktopMode = mInjector.canInternalDisplayHostDesktops(mContext);
    }

    /**
     * Decides which group type is needed for a given display content mode.
     * Locked on DisplayManager mSyncRoot.
     * Sets mReason, for logging purposes.
     */
    public String decideRequiredGroupTypeLocked(LogicalDisplay display, int type) {
        mReason = getContentModeForDisplayLocked(display, type);
        return switch (mReason) {
            case REASON_NON_DESKTOP, REASON_EXTENDED, REASON_FALLBACK -> GROUP_TYPE_PRIMARY;
            case REASON_PROJECTED -> GROUP_TYPE_SECONDARY;
            default -> GROUP_TYPE_PRIMARY;
        };
    }


    /**
     * Returns the first suitable group for a display to be sorted into, given the required group
     * type.
     * @param requiredGroupType which content mode group this display should be used in
     * @param displayGroups array of existing display groups.
     * @return Id of group that display should be sorted into
     */
    public static int calculateGroupId(String requiredGroupType,
            SparseArray<DisplayGroup> displayGroups) {
        // go through display groups, find one group with the correct tag.
        final int size = displayGroups.size();
        int calculatedGroup = Display.INVALID_DISPLAY_GROUP;
        for (int i = 0; i < size; i++) {
            final DisplayGroup displayGroup = displayGroups.valueAt(i);
            if (displayGroup.getGroupName().equals(requiredGroupType)) {
                calculatedGroup = displayGroups.keyAt(i);
                break;
            }
        }
        return calculatedGroup;
    }

    /**
     * Calculates whether this display supports desktop mode
     */
    public boolean isDesktopModeSupportedOnDisplayLocked(LogicalDisplay display, int type) {
        if (!mCanDeviceEnterDesktopMode) {
            return false;
        }

        if (type == Display.TYPE_INTERNAL) {
            return mCanDefaultDisplayEnterDesktopMode;
        }

        if (type == Display.TYPE_EXTERNAL || type == Display.TYPE_OVERLAY) {
            return mInjector.canDisplayHostTasksLocked(display);
        }
        return false;
    }

    /**
     * Decides on the content mode that the display should be using.
     */
    public int getContentModeForDisplayLocked(LogicalDisplay display, int type) {
        // conditions for non desktop mode;
        // desktop mode not supported on device or this display
        // or display is internal, and should be in a different group to the
        // projected and extended mode displays.
        if (!mCanDeviceEnterDesktopMode || type == Display.TYPE_INTERNAL) {
            return REASON_NON_DESKTOP;
        }

        // conditions for projected mode;
        // desktop mode supported on device, external display, but not internal
        if (mCanDeviceEnterDesktopMode
                && isDesktopModeSupportedOnDisplayLocked(display, type)
                && !mCanDefaultDisplayEnterDesktopMode) {
            return REASON_PROJECTED;
        }

        // conditions for extended mode;
        // desktop mode supported on both displays
        if (mCanDeviceEnterDesktopMode
                && isDesktopModeSupportedOnDisplayLocked(display, type)
                && mCanDefaultDisplayEnterDesktopMode) {
            return REASON_EXTENDED;
        }
        return REASON_FALLBACK;
    }

    public int getReason() {
        return mReason;
    }

    static class Injector {

        private boolean canEnterDesktopMode(Context context) {
            return DesktopModeHelper.canEnterDesktopMode(context);
        }

        boolean canInternalDisplayHostDesktops(Context context) {
            return DesktopModeHelper.canInternalDisplayHostDesktops(context);
        }

        boolean canDisplayHostTasksLocked(LogicalDisplay display) {
            return display.canHostTasksLocked();
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -1129,7 +1129,7 @@ final class LogicalDisplay {
    /**
     * Gets the name of display group to which the display is assigned.
     */
    public String getDisplayGroupNameLocked() {
    public String getLayoutGroupNameLocked() {
        return mDisplayGroupName;
    }

+38 −10
Original line number Diff line number Diff line
@@ -22,6 +22,9 @@ import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURAT
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static android.view.Display.DEFAULT_DISPLAY;

import static com.android.server.display.DisplayGroupAllocator.GROUP_TYPE_PRIMARY;
import static com.android.server.display.DisplayGroupAllocator.calculateGroupId;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -216,6 +219,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
    private final DisplayManagerFlags mFlags;
    private final SyntheticModeManager mSyntheticModeManager;
    private final FeatureFlags mDeviceStateManagerFlags;
    private final Context mContext;
    private final DisplayGroupAllocator mDisplayGroupAllocator;

    LogicalDisplayMapper(@NonNull Context context, FoldSettingProvider foldSettingProvider,
            FoldGracePeriodProvider foldGracePeriodProvider,
@@ -226,7 +231,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
                handler,
                new DeviceStateToLayoutMap((isDefault) -> isDefault ? DEFAULT_DISPLAY
                        : sNextNonDefaultDisplayId++, flags), flags,
                new SyntheticModeManager(flags));
                new SyntheticModeManager(flags), new DisplayGroupAllocator(context));
    }

    LogicalDisplayMapper(@NonNull Context context, FoldSettingProvider foldSettingProvider,
@@ -234,8 +239,10 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
            @NonNull DisplayDeviceRepository repo,
            @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
            @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap,
            DisplayManagerFlags flags, SyntheticModeManager syntheticModeManager) {
            DisplayManagerFlags flags, SyntheticModeManager syntheticModeManager,
            DisplayGroupAllocator displayGroupAllocator) {
        mSyncRoot = syncRoot;
        mContext = context;
        mPowerManager = context.getSystemService(PowerManager.class);
        mInteractive = mPowerManager.isInteractive();
        mHandler = new LogicalDisplayMapperHandler(handler.getLooper());
@@ -256,6 +263,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
        mFlags = flags;
        mSyntheticModeManager = syntheticModeManager;
        mDeviceStateManagerFlags = new FeatureFlagsImpl();
        mDisplayGroupAllocator = displayGroupAllocator;
    }

    @Override
@@ -586,6 +594,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
            if (!mDeviceStateToBeAppliedAfterBoot.equals(INVALID_DEVICE_STATE)) {
                setDeviceStateLocked(mDeviceStateToBeAppliedAfterBoot);
            }
            mDisplayGroupAllocator.initLater(mContext);
        }
    }

@@ -1077,12 +1086,25 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
        final DisplayGroup oldGroup = getDisplayGroupLocked(groupId);

        // groupName directly from LogicalDisplay (not from DisplayInfo)
        final String groupName = display.getDisplayGroupNameLocked();
        String groupName = display.getLayoutGroupNameLocked();
        // DisplayDeviceInfo is safe to use, it is updated earlier
        final DisplayDeviceInfo displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();

        int decidedGroupId = Display.INVALID_DISPLAY_GROUP;

        // Choose a display group based on the content mode type of the display.
        String requiredGroupType = GROUP_TYPE_PRIMARY;

        if (mFlags.isSeparateTimeoutsEnabled() && TextUtils.isEmpty(groupName)) {
            requiredGroupType = mDisplayGroupAllocator.decideRequiredGroupTypeLocked(
                    display, displayDeviceInfo.type);
            decidedGroupId = calculateGroupId(requiredGroupType, mDisplayGroups);
            groupName = requiredGroupType;
        }

        // Get the new display group if a change is needed, if display group name is empty and
        // {@code DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP} is not set, the display is assigned
        // to the default display group.
        // {@code DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP} is not set, and required group type
        // has not been decided, the display is assigned to the default display group.
        final boolean needsOwnDisplayGroup =
                (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0
                        || !TextUtils.isEmpty(groupName);
@@ -1094,17 +1116,19 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
                deviceDisplayGroupId != null && groupId == deviceDisplayGroupId;
        if (groupId == Display.INVALID_DISPLAY_GROUP
                || hasOwnDisplayGroup != needsOwnDisplayGroup
                || hasDeviceDisplayGroup != needsDeviceDisplayGroup) {
                || hasDeviceDisplayGroup != needsDeviceDisplayGroup
                || decidedGroupId != Display.INVALID_DISPLAY_GROUP) {
            groupId =
                    assignDisplayGroupIdLocked(needsOwnDisplayGroup,
                            display.getDisplayGroupNameLocked(), needsDeviceDisplayGroup,
                            linkedDeviceUniqueId);
                            display.getLayoutGroupNameLocked(), needsDeviceDisplayGroup,
                            linkedDeviceUniqueId, decidedGroupId);
        }

        // Create a new group if needed
        DisplayGroup newGroup = getDisplayGroupLocked(groupId);
        if (newGroup == null) {
            newGroup = new DisplayGroup(groupId);
            newGroup.setGroupName(groupName);
            mDisplayGroups.append(groupId, newGroup);
        }
        if (oldGroup != newGroup) {
@@ -1115,7 +1139,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
            display.updateDisplayGroupIdLocked(groupId);
            Slog.i(TAG, "Setting new display group " + groupId + " for display "
                    + displayId + ", from previous group: "
                    + (oldGroup != null ? oldGroup.getGroupId() : "null"));
                    + (oldGroup != null ? oldGroup.getGroupId() : "null")
                    + ", for reason: " + mDisplayGroupAllocator.getReason());
        }
    }

@@ -1314,7 +1339,10 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
    }

    private int assignDisplayGroupIdLocked(boolean isOwnDisplayGroup, String displayGroupName,
            boolean isDeviceDisplayGroup, Integer linkedDeviceUniqueId) {
            boolean isDeviceDisplayGroup, Integer linkedDeviceUniqueId, int decidedGroupId) {
        if (decidedGroupId != Display.INVALID_DISPLAY_GROUP) {
            return decidedGroupId;
        }
        if (isDeviceDisplayGroup && linkedDeviceUniqueId != null) {
            int deviceDisplayGroupId = mDeviceDisplayGroupIds.get(linkedDeviceUniqueId);
            // A value of 0 indicates that no device display group was found.
+3 −3
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import com.android.window.flags.Flags;
 * Constants for desktop mode feature
 */
public final class DesktopModeHelper {

    /**
     * Flag to indicate whether to restrict desktop mode to supported devices.
     */
@@ -66,8 +67,7 @@ public final class DesktopModeHelper {
    /**
     * Return {@code true} if the current device can hosts desktop sessions on its internal display.
     */
    @VisibleForTesting
    private static boolean canInternalDisplayHostDesktops(@NonNull Context context) {
    public static boolean canInternalDisplayHostDesktops(@NonNull Context context) {
        return context.getResources().getBoolean(R.bool.config_canInternalDisplayHostDesktops);
    }

@@ -98,7 +98,7 @@ public final class DesktopModeHelper {
    /**
     * Return {@code true} if desktop mode can be entered on the current device.
     */
    static boolean canEnterDesktopMode(@NonNull Context context) {
    public static boolean canEnterDesktopMode(@NonNull Context context) {
        return (isDeviceEligibleForDesktopMode(context)
                && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue())
                || isDesktopModeEnabledByDevOption(context);
Loading