Loading services/core/java/com/android/server/display/DeviceStateToLayoutMap.java +8 −0 Original line number Diff line number Diff line Loading @@ -112,6 +112,14 @@ class DeviceStateToLayoutMap { return layout; } void put(int state, Layout layout) { mLayoutMap.put(state, layout); } void remove(int state) { mLayoutMap.remove(state); } int size() { return mLayoutMap.size(); } Loading services/core/java/com/android/server/display/DisplayManagerService.java +28 −24 Original line number Diff line number Diff line Loading @@ -218,6 +218,7 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.function.Predicate; /** * Manages attached displays. Loading Loading @@ -658,9 +659,33 @@ public final class DisplayManagerService extends SystemService { mUiHandler = UiThread.getHandler(); mPersistentDataStore = mInjector.getPersistentDataStore(); mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore); mLogicalDisplayMapper = new LogicalDisplayMapper(mContext, foldSettingProvider, mDisplayDeviceRepo, new LogicalDisplayListener(), mSyncRoot, mHandler, mFlags); if (mFlags.isDisplayTopologyEnabled()) { final var backupManager = new BackupManager(mContext); Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> topologyChangedCallback = update -> { if (mInputManagerInternal != null) { Slog.d(TAG, "Sending topology graph to Input Manager: " + update.second); mInputManagerInternal.setDisplayTopology(update.second); } else { Slog.w(TAG, "Not sending topology, mInputManagerInternal is null"); } deliverTopologyUpdate(update.first); }; mDisplayTopologyCoordinator = new DisplayTopologyCoordinator( this::isExtendedDisplayAllowed, this::shouldIncludeDefaultDisplayInTopology, topologyChangedCallback, new HandlerExecutor(mHandler), mSyncRoot, backupManager::dataChanged, mFlags, displayId -> getDisplayInfoInternal(displayId, Process.myUid())); } else { mDisplayTopologyCoordinator = null; } Predicate<DisplayInfo> isDisplayAllowedInTopoogy = info -> mDisplayTopologyCoordinator != null && mDisplayTopologyCoordinator.isDisplayAllowedInTopology(info); mLogicalDisplayMapper = new LogicalDisplayMapper(mContext, foldSettingProvider, mDisplayDeviceRepo, new LogicalDisplayListener(), mSyncRoot, mHandler, mFlags, isDisplayAllowedInTopoogy); mDisplayModeDirector = new DisplayModeDirector( context, mHandler, mFlags, mDisplayDeviceConfigProvider); mBrightnessSynchronizer = new BrightnessSynchronizer(mContext, displayThreadLooper); Loading Loading @@ -692,27 +717,6 @@ public final class DisplayManagerService extends SystemService { mDisplayNotificationManager = new DisplayNotificationManager(mFlags, mContext, mExternalDisplayStatsService); mExternalDisplayPolicy = new ExternalDisplayPolicy(new ExternalDisplayPolicyInjector()); if (mFlags.isDisplayTopologyEnabled()) { final var backupManager = new BackupManager(mContext); Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> topologyChangedCallback = update -> { if (mInputManagerInternal != null) { Slog.d(TAG, "Sending topology graph to Input Manager: " + update.second); mInputManagerInternal.setDisplayTopology(update.second); } else { Slog.w(TAG, "Not sending topology, mInputManagerInternal is null"); } deliverTopologyUpdate(update.first); }; mDisplayTopologyCoordinator = new DisplayTopologyCoordinator( this::isExtendedDisplayAllowed, this::shouldIncludeDefaultDisplayInTopology, topologyChangedCallback, new HandlerExecutor(mHandler), mSyncRoot, backupManager::dataChanged, mFlags, displayId -> getDisplayInfoInternal(displayId, Process.myUid())); } else { mDisplayTopologyCoordinator = null; } mPluginManager = new PluginManager(mContext, mFlags); } Loading services/core/java/com/android/server/display/DisplayTopologyCoordinator.java +5 −1 Original line number Diff line number Diff line Loading @@ -151,7 +151,7 @@ class DisplayTopologyCoordinator { * @param info The new display info */ void onDisplayChanged(DisplayInfo info) { if (!isDisplayAllowedInTopology(info, /* shouldLog= */ false)) { if (!isDisplayAllowedInTopology(info)) { return; } synchronized (mSyncRoot) { Loading Loading @@ -276,6 +276,10 @@ class DisplayTopologyCoordinator { mDisplayIdToUniqueIdMapping.put(info.displayId, uniqueId); } boolean isDisplayAllowedInTopology(DisplayInfo info) { return isDisplayAllowedInTopology(info, /* shouldLog= */ false); } private boolean isDisplayAllowedInTopology(DisplayInfo info, boolean shouldLog) { if (info == null) { return false; Loading services/core/java/com/android/server/display/LogicalDisplayMapper.java +146 −36 Original line number Diff line number Diff line Loading @@ -17,15 +17,20 @@ package com.android.server.display; import static android.hardware.devicestate.DeviceState.PROPERTY_EMULATED_ONLY; import static android.hardware.devicestate.DeviceState.PROPERTY_LAPTOP_HARDWARE_CONFIGURATION_LID_CLOSED; import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP; import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.server.display.DeviceStateToLayoutMap.STATE_DEFAULT; import static com.android.server.display.DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY; import static com.android.server.display.DisplayGroupAllocator.GROUP_TYPE_PRIMARY; import static com.android.server.display.DisplayGroupAllocator.REASON_EXTENDED; import static com.android.server.display.DisplayGroupAllocator.REASON_PROJECTED; import static com.android.server.display.DisplayGroupAllocator.calculateGroupId; import static com.android.server.display.layout.Layout.DEFAULT_DISPLAY_GROUP_NAME; import static com.android.server.display.layout.Layout.Display.POSITION_UNKNOWN; import android.annotation.NonNull; import android.annotation.Nullable; Loading Loading @@ -54,6 +59,7 @@ import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.feature.flags.Flags; import com.android.server.display.layout.DisplayIdProducer; import com.android.server.display.layout.Layout; import com.android.server.display.mode.SyntheticModeManager; Loading @@ -64,6 +70,7 @@ import com.android.server.utils.FoldSettingProvider; import java.io.PrintWriter; import java.util.Arrays; import java.util.function.Consumer; import java.util.function.Predicate; /** * Responsible for creating {@link LogicalDisplay}s and associating them to the Loading Loading @@ -221,16 +228,18 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private final FeatureFlags mDeviceStateManagerFlags; private final Context mContext; private final DisplayGroupAllocator mDisplayGroupAllocator; private final Predicate<DisplayInfo> mIsDisplayAllowedInTopology; LogicalDisplayMapper(@NonNull Context context, FoldSettingProvider foldSettingProvider, @NonNull DisplayDeviceRepository repo, @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, @NonNull Handler handler, DisplayManagerFlags flags) { this(context, foldSettingProvider, repo, listener, syncRoot, handler, new DeviceStateToLayoutMap((isDefault) -> isDefault ? DEFAULT_DISPLAY : sNextNonDefaultDisplayId++, flags), flags, new SyntheticModeManager(flags), new DisplayGroupAllocator(context)); @NonNull Handler handler, DisplayManagerFlags flags, Predicate<DisplayInfo> isDisplayAllowedInTopology) { this(context, foldSettingProvider, repo, listener, syncRoot, handler, new DeviceStateToLayoutMap( (isDefault) -> isDefault ? DEFAULT_DISPLAY : sNextNonDefaultDisplayId++, flags), flags, new SyntheticModeManager(flags), new DisplayGroupAllocator( context), isDisplayAllowedInTopology); } LogicalDisplayMapper(@NonNull Context context, FoldSettingProvider foldSettingProvider, Loading @@ -238,7 +247,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap, DisplayManagerFlags flags, SyntheticModeManager syntheticModeManager, DisplayGroupAllocator displayGroupAllocator) { DisplayGroupAllocator displayGroupAllocator, Predicate<DisplayInfo> isDisplayAllowedInTopology) { mSyncRoot = syncRoot; mContext = context; mPowerManager = context.getSystemService(PowerManager.class); Loading @@ -261,6 +271,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mSyntheticModeManager = syntheticModeManager; mDeviceStateManagerFlags = new FeatureFlagsImpl(); mDisplayGroupAllocator = displayGroupAllocator; mIsDisplayAllowedInTopology = isDisplayAllowedInTopology; } @Override Loading Loading @@ -526,6 +537,11 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { return; } if (Flags.changeDefaultDisplayLidClosed() && state.hasProperty( PROPERTY_LAPTOP_HARDWARE_CONFIGURATION_LID_CLOSED)) { createLayoutWithDefaultSecondaryDisplayLocked(state.getIdentifier()); } // As part of a state transition, we may need to turn off some displays temporarily so that // the transition is smooth. Plus, on some devices, only one internal displays can be // on at a time. We use LogicalDisplay.setIsInTransition to mark a display that needs to be Loading Loading @@ -739,7 +755,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { // This initializes a default dynamic display layout for the default // device, which is used as a fallback in case no static layout definitions // exist or cannot be loaded. if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0) { if ((deviceInfo.flags & FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0) { initializeDefaultDisplayDeviceLocked(device); } Loading @@ -752,36 +768,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } private void handleDisplayDeviceRemovedLocked(DisplayDevice device) { final Layout layout = mDeviceStateToLayoutMap.get(DeviceStateToLayoutMap.STATE_DEFAULT); Layout.Display layoutDisplay = layout.getById(DEFAULT_DISPLAY); if (layoutDisplay == null) { return; } DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked(); // Remove any virtual device mapping which exists for the display. mVirtualDeviceDisplayMapping.remove(device.getUniqueId()); if (layoutDisplay.getAddress().equals(deviceInfo.address)) { layout.removeDisplayLocked(DEFAULT_DISPLAY); // Need to find another local display and make it default for (int i = 0; i < mLogicalDisplays.size(); i++) { LogicalDisplay nextDisplay = mLogicalDisplays.valueAt(i); DisplayDevice nextDevice = nextDisplay.getPrimaryDisplayDeviceLocked(); if (nextDevice == null) { continue; } DisplayDeviceInfo nextDeviceInfo = nextDevice.getDisplayDeviceInfoLocked(); if ((nextDeviceInfo.flags & DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0 && !nextDeviceInfo.address.equals(deviceInfo.address)) { layout.createDefaultDisplayLocked(nextDeviceInfo.address, mIdProducer); if (findNewDefaultSecondaryDisplayIfNeededLocked(device) || findNewDefaultDisplayIfNeededLocked(device)) { applyLayoutLocked(); return; } } } } Loading Loading @@ -1376,7 +1368,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { // display for the default display device that is found. // To that end, when we are notified of a new default display, we add it to // the default layout definition if it is not already there. final Layout layout = mDeviceStateToLayoutMap.get(DeviceStateToLayoutMap.STATE_DEFAULT); final Layout layout = mDeviceStateToLayoutMap.get(STATE_DEFAULT); if (layout.getById(DEFAULT_DISPLAY) != null) { // The layout should only have one default display return; Loading Loading @@ -1449,6 +1441,124 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { updateLogicalDisplaysLocked(); } private void createLayoutWithDefaultSecondaryDisplayLocked(int identifier) { for (int i = 0; i < mLogicalDisplays.size(); i++) { LogicalDisplay display = mLogicalDisplays.valueAt(i); if (isDisplayAllowedToBeDefaultSecondaryLocked(display)) { Layout layout = new Layout(); // Default, enabled secondary display layout.createDefaultDisplayLocked(display.getPrimaryDisplayDeviceLocked() .getDisplayDeviceInfoLocked().address, mIdProducer); // Disabled internal display layout.createDisplayLocked(mDeviceStateToLayoutMap.get(STATE_DEFAULT).getById( DEFAULT_DISPLAY).getAddress(), /* isDefault= */ false, /* isEnabled= */ false, DEFAULT_DISPLAY_GROUP_NAME, mIdProducer, POSITION_UNKNOWN, /* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null, /* powerThrottlingMapId= */ null); mDeviceStateToLayoutMap.put(identifier, layout); break; } } } private boolean isDisplayAllowedToBeDefaultSecondaryLocked(LogicalDisplay display) { DisplayDevice device = display.getPrimaryDisplayDeviceLocked(); if (device == null) { return false; } DisplayInfo displayInfo = display.getDisplayInfoLocked(); return displayInfo.type != Display.TYPE_INTERNAL && display.isEnabledLocked() && display.canHostTasksLocked() && mIsDisplayAllowedInTopology.test(displayInfo) && (device.getDisplayDeviceInfoLocked().flags & FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0; } /** * Find a new default secondary display if the current one is being removed. If none can be * found, remove the closed-lid state layout so that the default layout is applied instead. * @param removedDisplayDevice The display device being removed * @return True if the current layout has been modified and needs to be re-applied. */ private boolean findNewDefaultSecondaryDisplayIfNeededLocked( DisplayDevice removedDisplayDevice) { if (!Flags.changeDefaultDisplayLidClosed() || !mDeviceState.hasProperty( PROPERTY_LAPTOP_HARDWARE_CONFIGURATION_LID_CLOSED)) { return false; } final Layout layout = mDeviceStateToLayoutMap.get(mDeviceState.getIdentifier()); Layout.Display layoutDefaultDisplay = layout.getById(DEFAULT_DISPLAY); DisplayDeviceInfo removedDeviceInfo = removedDisplayDevice.getDisplayDeviceInfoLocked(); if (layoutDefaultDisplay == null || !layoutDefaultDisplay.getAddress().equals( removedDeviceInfo.address)) { return false; } layout.removeDisplayLocked(DEFAULT_DISPLAY); // Need to find another secondary display and make it default boolean found = false; for (int i = 0; i < mLogicalDisplays.size(); i++) { LogicalDisplay nextDisplay = mLogicalDisplays.valueAt(i); DisplayDevice nextDevice = nextDisplay.getPrimaryDisplayDeviceLocked(); if (nextDevice == null) { continue; } DisplayDeviceInfo nextDeviceInfo = nextDevice.getDisplayDeviceInfoLocked(); if (isDisplayAllowedToBeDefaultSecondaryLocked(nextDisplay) && !nextDeviceInfo.address.equals(removedDeviceInfo.address)) { layout.createDefaultDisplayLocked(nextDeviceInfo.address, mIdProducer); found = true; break; } } if (!found) { // If no secondary display can be default, go back to the default layout mDeviceStateToLayoutMap.remove(mDeviceState.getIdentifier()); } return true; } /** * Find a new default display if the current one is being removed. * @param removedDisplayDevice The display device being removed * @return True if the current layout has been modified and needs to be re-applied. */ private boolean findNewDefaultDisplayIfNeededLocked(DisplayDevice removedDisplayDevice) { final Layout layout = mDeviceStateToLayoutMap.get(STATE_DEFAULT); Layout.Display layoutDefaultDisplay = layout.getById(DEFAULT_DISPLAY); DisplayDeviceInfo removedDeviceInfo = removedDisplayDevice.getDisplayDeviceInfoLocked(); if (layoutDefaultDisplay == null || !layoutDefaultDisplay.getAddress().equals( removedDeviceInfo.address)) { return false; } layout.removeDisplayLocked(DEFAULT_DISPLAY); // Need to find another local display and make it default for (int i = 0; i < mLogicalDisplays.size(); i++) { LogicalDisplay nextDisplay = mLogicalDisplays.valueAt(i); DisplayDevice nextDevice = nextDisplay.getPrimaryDisplayDeviceLocked(); if (nextDevice == null) { continue; } DisplayDeviceInfo nextDeviceInfo = nextDevice.getDisplayDeviceInfoLocked(); if ((nextDeviceInfo.flags & DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0 && !nextDeviceInfo.address.equals(removedDeviceInfo.address)) { layout.createDefaultDisplayLocked(nextDeviceInfo.address, mIdProducer); return true; } } return false; } public interface Listener { void onLogicalDisplayEventLocked(LogicalDisplay display, int event); void onDisplayGroupEventLocked(int groupId, int event); Loading services/core/java/com/android/server/display/feature/display_flags.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,13 @@ flag { is_exported: true } flag { name: "change_default_display_lid_closed" namespace: "display_manager" description: "Make one of the secondary displays the default display when the device's lid is closed" bug: "400351704" } flag { name: "enable_display_offload" namespace: "display_manager" Loading Loading
services/core/java/com/android/server/display/DeviceStateToLayoutMap.java +8 −0 Original line number Diff line number Diff line Loading @@ -112,6 +112,14 @@ class DeviceStateToLayoutMap { return layout; } void put(int state, Layout layout) { mLayoutMap.put(state, layout); } void remove(int state) { mLayoutMap.remove(state); } int size() { return mLayoutMap.size(); } Loading
services/core/java/com/android/server/display/DisplayManagerService.java +28 −24 Original line number Diff line number Diff line Loading @@ -218,6 +218,7 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.function.Predicate; /** * Manages attached displays. Loading Loading @@ -658,9 +659,33 @@ public final class DisplayManagerService extends SystemService { mUiHandler = UiThread.getHandler(); mPersistentDataStore = mInjector.getPersistentDataStore(); mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore); mLogicalDisplayMapper = new LogicalDisplayMapper(mContext, foldSettingProvider, mDisplayDeviceRepo, new LogicalDisplayListener(), mSyncRoot, mHandler, mFlags); if (mFlags.isDisplayTopologyEnabled()) { final var backupManager = new BackupManager(mContext); Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> topologyChangedCallback = update -> { if (mInputManagerInternal != null) { Slog.d(TAG, "Sending topology graph to Input Manager: " + update.second); mInputManagerInternal.setDisplayTopology(update.second); } else { Slog.w(TAG, "Not sending topology, mInputManagerInternal is null"); } deliverTopologyUpdate(update.first); }; mDisplayTopologyCoordinator = new DisplayTopologyCoordinator( this::isExtendedDisplayAllowed, this::shouldIncludeDefaultDisplayInTopology, topologyChangedCallback, new HandlerExecutor(mHandler), mSyncRoot, backupManager::dataChanged, mFlags, displayId -> getDisplayInfoInternal(displayId, Process.myUid())); } else { mDisplayTopologyCoordinator = null; } Predicate<DisplayInfo> isDisplayAllowedInTopoogy = info -> mDisplayTopologyCoordinator != null && mDisplayTopologyCoordinator.isDisplayAllowedInTopology(info); mLogicalDisplayMapper = new LogicalDisplayMapper(mContext, foldSettingProvider, mDisplayDeviceRepo, new LogicalDisplayListener(), mSyncRoot, mHandler, mFlags, isDisplayAllowedInTopoogy); mDisplayModeDirector = new DisplayModeDirector( context, mHandler, mFlags, mDisplayDeviceConfigProvider); mBrightnessSynchronizer = new BrightnessSynchronizer(mContext, displayThreadLooper); Loading Loading @@ -692,27 +717,6 @@ public final class DisplayManagerService extends SystemService { mDisplayNotificationManager = new DisplayNotificationManager(mFlags, mContext, mExternalDisplayStatsService); mExternalDisplayPolicy = new ExternalDisplayPolicy(new ExternalDisplayPolicyInjector()); if (mFlags.isDisplayTopologyEnabled()) { final var backupManager = new BackupManager(mContext); Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> topologyChangedCallback = update -> { if (mInputManagerInternal != null) { Slog.d(TAG, "Sending topology graph to Input Manager: " + update.second); mInputManagerInternal.setDisplayTopology(update.second); } else { Slog.w(TAG, "Not sending topology, mInputManagerInternal is null"); } deliverTopologyUpdate(update.first); }; mDisplayTopologyCoordinator = new DisplayTopologyCoordinator( this::isExtendedDisplayAllowed, this::shouldIncludeDefaultDisplayInTopology, topologyChangedCallback, new HandlerExecutor(mHandler), mSyncRoot, backupManager::dataChanged, mFlags, displayId -> getDisplayInfoInternal(displayId, Process.myUid())); } else { mDisplayTopologyCoordinator = null; } mPluginManager = new PluginManager(mContext, mFlags); } Loading
services/core/java/com/android/server/display/DisplayTopologyCoordinator.java +5 −1 Original line number Diff line number Diff line Loading @@ -151,7 +151,7 @@ class DisplayTopologyCoordinator { * @param info The new display info */ void onDisplayChanged(DisplayInfo info) { if (!isDisplayAllowedInTopology(info, /* shouldLog= */ false)) { if (!isDisplayAllowedInTopology(info)) { return; } synchronized (mSyncRoot) { Loading Loading @@ -276,6 +276,10 @@ class DisplayTopologyCoordinator { mDisplayIdToUniqueIdMapping.put(info.displayId, uniqueId); } boolean isDisplayAllowedInTopology(DisplayInfo info) { return isDisplayAllowedInTopology(info, /* shouldLog= */ false); } private boolean isDisplayAllowedInTopology(DisplayInfo info, boolean shouldLog) { if (info == null) { return false; Loading
services/core/java/com/android/server/display/LogicalDisplayMapper.java +146 −36 Original line number Diff line number Diff line Loading @@ -17,15 +17,20 @@ package com.android.server.display; import static android.hardware.devicestate.DeviceState.PROPERTY_EMULATED_ONLY; import static android.hardware.devicestate.DeviceState.PROPERTY_LAPTOP_HARDWARE_CONFIGURATION_LID_CLOSED; import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP; import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.server.display.DeviceStateToLayoutMap.STATE_DEFAULT; import static com.android.server.display.DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY; import static com.android.server.display.DisplayGroupAllocator.GROUP_TYPE_PRIMARY; import static com.android.server.display.DisplayGroupAllocator.REASON_EXTENDED; import static com.android.server.display.DisplayGroupAllocator.REASON_PROJECTED; import static com.android.server.display.DisplayGroupAllocator.calculateGroupId; import static com.android.server.display.layout.Layout.DEFAULT_DISPLAY_GROUP_NAME; import static com.android.server.display.layout.Layout.Display.POSITION_UNKNOWN; import android.annotation.NonNull; import android.annotation.Nullable; Loading Loading @@ -54,6 +59,7 @@ import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.feature.flags.Flags; import com.android.server.display.layout.DisplayIdProducer; import com.android.server.display.layout.Layout; import com.android.server.display.mode.SyntheticModeManager; Loading @@ -64,6 +70,7 @@ import com.android.server.utils.FoldSettingProvider; import java.io.PrintWriter; import java.util.Arrays; import java.util.function.Consumer; import java.util.function.Predicate; /** * Responsible for creating {@link LogicalDisplay}s and associating them to the Loading Loading @@ -221,16 +228,18 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private final FeatureFlags mDeviceStateManagerFlags; private final Context mContext; private final DisplayGroupAllocator mDisplayGroupAllocator; private final Predicate<DisplayInfo> mIsDisplayAllowedInTopology; LogicalDisplayMapper(@NonNull Context context, FoldSettingProvider foldSettingProvider, @NonNull DisplayDeviceRepository repo, @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, @NonNull Handler handler, DisplayManagerFlags flags) { this(context, foldSettingProvider, repo, listener, syncRoot, handler, new DeviceStateToLayoutMap((isDefault) -> isDefault ? DEFAULT_DISPLAY : sNextNonDefaultDisplayId++, flags), flags, new SyntheticModeManager(flags), new DisplayGroupAllocator(context)); @NonNull Handler handler, DisplayManagerFlags flags, Predicate<DisplayInfo> isDisplayAllowedInTopology) { this(context, foldSettingProvider, repo, listener, syncRoot, handler, new DeviceStateToLayoutMap( (isDefault) -> isDefault ? DEFAULT_DISPLAY : sNextNonDefaultDisplayId++, flags), flags, new SyntheticModeManager(flags), new DisplayGroupAllocator( context), isDisplayAllowedInTopology); } LogicalDisplayMapper(@NonNull Context context, FoldSettingProvider foldSettingProvider, Loading @@ -238,7 +247,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap, DisplayManagerFlags flags, SyntheticModeManager syntheticModeManager, DisplayGroupAllocator displayGroupAllocator) { DisplayGroupAllocator displayGroupAllocator, Predicate<DisplayInfo> isDisplayAllowedInTopology) { mSyncRoot = syncRoot; mContext = context; mPowerManager = context.getSystemService(PowerManager.class); Loading @@ -261,6 +271,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mSyntheticModeManager = syntheticModeManager; mDeviceStateManagerFlags = new FeatureFlagsImpl(); mDisplayGroupAllocator = displayGroupAllocator; mIsDisplayAllowedInTopology = isDisplayAllowedInTopology; } @Override Loading Loading @@ -526,6 +537,11 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { return; } if (Flags.changeDefaultDisplayLidClosed() && state.hasProperty( PROPERTY_LAPTOP_HARDWARE_CONFIGURATION_LID_CLOSED)) { createLayoutWithDefaultSecondaryDisplayLocked(state.getIdentifier()); } // As part of a state transition, we may need to turn off some displays temporarily so that // the transition is smooth. Plus, on some devices, only one internal displays can be // on at a time. We use LogicalDisplay.setIsInTransition to mark a display that needs to be Loading Loading @@ -739,7 +755,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { // This initializes a default dynamic display layout for the default // device, which is used as a fallback in case no static layout definitions // exist or cannot be loaded. if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0) { if ((deviceInfo.flags & FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0) { initializeDefaultDisplayDeviceLocked(device); } Loading @@ -752,36 +768,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } private void handleDisplayDeviceRemovedLocked(DisplayDevice device) { final Layout layout = mDeviceStateToLayoutMap.get(DeviceStateToLayoutMap.STATE_DEFAULT); Layout.Display layoutDisplay = layout.getById(DEFAULT_DISPLAY); if (layoutDisplay == null) { return; } DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked(); // Remove any virtual device mapping which exists for the display. mVirtualDeviceDisplayMapping.remove(device.getUniqueId()); if (layoutDisplay.getAddress().equals(deviceInfo.address)) { layout.removeDisplayLocked(DEFAULT_DISPLAY); // Need to find another local display and make it default for (int i = 0; i < mLogicalDisplays.size(); i++) { LogicalDisplay nextDisplay = mLogicalDisplays.valueAt(i); DisplayDevice nextDevice = nextDisplay.getPrimaryDisplayDeviceLocked(); if (nextDevice == null) { continue; } DisplayDeviceInfo nextDeviceInfo = nextDevice.getDisplayDeviceInfoLocked(); if ((nextDeviceInfo.flags & DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0 && !nextDeviceInfo.address.equals(deviceInfo.address)) { layout.createDefaultDisplayLocked(nextDeviceInfo.address, mIdProducer); if (findNewDefaultSecondaryDisplayIfNeededLocked(device) || findNewDefaultDisplayIfNeededLocked(device)) { applyLayoutLocked(); return; } } } } Loading Loading @@ -1376,7 +1368,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { // display for the default display device that is found. // To that end, when we are notified of a new default display, we add it to // the default layout definition if it is not already there. final Layout layout = mDeviceStateToLayoutMap.get(DeviceStateToLayoutMap.STATE_DEFAULT); final Layout layout = mDeviceStateToLayoutMap.get(STATE_DEFAULT); if (layout.getById(DEFAULT_DISPLAY) != null) { // The layout should only have one default display return; Loading Loading @@ -1449,6 +1441,124 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { updateLogicalDisplaysLocked(); } private void createLayoutWithDefaultSecondaryDisplayLocked(int identifier) { for (int i = 0; i < mLogicalDisplays.size(); i++) { LogicalDisplay display = mLogicalDisplays.valueAt(i); if (isDisplayAllowedToBeDefaultSecondaryLocked(display)) { Layout layout = new Layout(); // Default, enabled secondary display layout.createDefaultDisplayLocked(display.getPrimaryDisplayDeviceLocked() .getDisplayDeviceInfoLocked().address, mIdProducer); // Disabled internal display layout.createDisplayLocked(mDeviceStateToLayoutMap.get(STATE_DEFAULT).getById( DEFAULT_DISPLAY).getAddress(), /* isDefault= */ false, /* isEnabled= */ false, DEFAULT_DISPLAY_GROUP_NAME, mIdProducer, POSITION_UNKNOWN, /* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null, /* powerThrottlingMapId= */ null); mDeviceStateToLayoutMap.put(identifier, layout); break; } } } private boolean isDisplayAllowedToBeDefaultSecondaryLocked(LogicalDisplay display) { DisplayDevice device = display.getPrimaryDisplayDeviceLocked(); if (device == null) { return false; } DisplayInfo displayInfo = display.getDisplayInfoLocked(); return displayInfo.type != Display.TYPE_INTERNAL && display.isEnabledLocked() && display.canHostTasksLocked() && mIsDisplayAllowedInTopology.test(displayInfo) && (device.getDisplayDeviceInfoLocked().flags & FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0; } /** * Find a new default secondary display if the current one is being removed. If none can be * found, remove the closed-lid state layout so that the default layout is applied instead. * @param removedDisplayDevice The display device being removed * @return True if the current layout has been modified and needs to be re-applied. */ private boolean findNewDefaultSecondaryDisplayIfNeededLocked( DisplayDevice removedDisplayDevice) { if (!Flags.changeDefaultDisplayLidClosed() || !mDeviceState.hasProperty( PROPERTY_LAPTOP_HARDWARE_CONFIGURATION_LID_CLOSED)) { return false; } final Layout layout = mDeviceStateToLayoutMap.get(mDeviceState.getIdentifier()); Layout.Display layoutDefaultDisplay = layout.getById(DEFAULT_DISPLAY); DisplayDeviceInfo removedDeviceInfo = removedDisplayDevice.getDisplayDeviceInfoLocked(); if (layoutDefaultDisplay == null || !layoutDefaultDisplay.getAddress().equals( removedDeviceInfo.address)) { return false; } layout.removeDisplayLocked(DEFAULT_DISPLAY); // Need to find another secondary display and make it default boolean found = false; for (int i = 0; i < mLogicalDisplays.size(); i++) { LogicalDisplay nextDisplay = mLogicalDisplays.valueAt(i); DisplayDevice nextDevice = nextDisplay.getPrimaryDisplayDeviceLocked(); if (nextDevice == null) { continue; } DisplayDeviceInfo nextDeviceInfo = nextDevice.getDisplayDeviceInfoLocked(); if (isDisplayAllowedToBeDefaultSecondaryLocked(nextDisplay) && !nextDeviceInfo.address.equals(removedDeviceInfo.address)) { layout.createDefaultDisplayLocked(nextDeviceInfo.address, mIdProducer); found = true; break; } } if (!found) { // If no secondary display can be default, go back to the default layout mDeviceStateToLayoutMap.remove(mDeviceState.getIdentifier()); } return true; } /** * Find a new default display if the current one is being removed. * @param removedDisplayDevice The display device being removed * @return True if the current layout has been modified and needs to be re-applied. */ private boolean findNewDefaultDisplayIfNeededLocked(DisplayDevice removedDisplayDevice) { final Layout layout = mDeviceStateToLayoutMap.get(STATE_DEFAULT); Layout.Display layoutDefaultDisplay = layout.getById(DEFAULT_DISPLAY); DisplayDeviceInfo removedDeviceInfo = removedDisplayDevice.getDisplayDeviceInfoLocked(); if (layoutDefaultDisplay == null || !layoutDefaultDisplay.getAddress().equals( removedDeviceInfo.address)) { return false; } layout.removeDisplayLocked(DEFAULT_DISPLAY); // Need to find another local display and make it default for (int i = 0; i < mLogicalDisplays.size(); i++) { LogicalDisplay nextDisplay = mLogicalDisplays.valueAt(i); DisplayDevice nextDevice = nextDisplay.getPrimaryDisplayDeviceLocked(); if (nextDevice == null) { continue; } DisplayDeviceInfo nextDeviceInfo = nextDevice.getDisplayDeviceInfoLocked(); if ((nextDeviceInfo.flags & DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0 && !nextDeviceInfo.address.equals(removedDeviceInfo.address)) { layout.createDefaultDisplayLocked(nextDeviceInfo.address, mIdProducer); return true; } } return false; } public interface Listener { void onLogicalDisplayEventLocked(LogicalDisplay display, int event); void onDisplayGroupEventLocked(int groupId, int event); Loading
services/core/java/com/android/server/display/feature/display_flags.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,13 @@ flag { is_exported: true } flag { name: "change_default_display_lid_closed" namespace: "display_manager" description: "Make one of the secondary displays the default display when the device's lid is closed" bug: "400351704" } flag { name: "enable_display_offload" namespace: "display_manager" Loading