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

Commit 05e1c1a9 authored by Santos Cordon's avatar Santos Cordon
Browse files

Add Logical Display Mapper.

Move the LogicalDisplay creation and management code out of
DisplayManagerService and into LogicalDisplayMapper.  DMS stops
listening to DisplayDevice events directly and now only reacts to
events from LogicalDisplayMapper.

Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/display
Bug: 130724654
Change-Id: I8c8bf94ce0ab85d27f3bbe51f67a1ff75315e09b
parent d025d3b4
Loading
Loading
Loading
Loading
+52 −10
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.display;

import android.annotation.NonNull;
import android.util.Slog;
import android.view.Display;

import com.android.internal.annotations.GuardedBy;
import com.android.server.display.DisplayManagerService.SyncRoot;
@@ -48,15 +49,27 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener {
    @GuardedBy("mSyncRoot")
    private final List<DisplayDevice> mDisplayDevices = new ArrayList<>();

    /** Listener for {link DisplayDevice} events. */
    private final Listener mListener;
    /** Listeners for {link DisplayDevice} events. */
    @GuardedBy("mSyncRoot")
    private final List<Listener> mListeners = new ArrayList<>();

    /** Global lock object from {@link DisplayManagerService}. */
    private final SyncRoot mSyncRoot;

    DisplayDeviceRepository(@NonNull SyncRoot syncRoot, @NonNull Listener listener) {
    private final PersistentDataStore mPersistentDataStore;

    /**
     * @param syncRoot The global lock for DisplayManager related objects.
     * @param persistentDataStore Settings data store from {@link DisplayManagerService}.
     */
    DisplayDeviceRepository(@NonNull SyncRoot syncRoot,
            @NonNull PersistentDataStore persistentDataStore) {
        mSyncRoot = syncRoot;
        mListener = listener;
        mPersistentDataStore = persistentDataStore;
    }

    public void addListener(@NonNull Listener listener) {
        mListeners.add(listener);
    }

    @Override
@@ -78,7 +91,10 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener {

    @Override
    public void onTraversalRequested() {
        mListener.onTraversalRequested();
        final int size = mListeners.size();
        for (int i = 0; i < size; i++) {
            mListeners.get(i).onTraversalRequested();
        }
    }

    public boolean containsLocked(DisplayDevice d) {
@@ -107,18 +123,37 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener {
            device.mDebugLastLoggedDeviceInfo = info;

            mDisplayDevices.add(device);
            mListener.onDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
            sendEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
        }
    }

    private void handleDisplayDeviceChanged(DisplayDevice device) {
        synchronized (mSyncRoot) {
            DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
            final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
            if (!mDisplayDevices.contains(device)) {
                Slog.w(TAG, "Attempted to change non-existent display device: " + info);
                return;
            }
            mListener.onDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);

            int diff = device.mDebugLastLoggedDeviceInfo.diff(info);
            if (diff == DisplayDeviceInfo.DIFF_STATE) {
                Slog.i(TAG, "Display device changed state: \"" + info.name
                        + "\", " + Display.stateToString(info.state));
            } else if (diff != 0) {
                Slog.i(TAG, "Display device changed: " + info);
            }

            if ((diff & DisplayDeviceInfo.DIFF_COLOR_MODE) != 0) {
                try {
                    mPersistentDataStore.setColorMode(device, info.colorMode);
                } finally {
                    mPersistentDataStore.saveIfNeeded();
                }
            }
            device.mDebugLastLoggedDeviceInfo = info;

            device.applyPendingDisplayDeviceInfoChangesLocked();
            sendEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
        }
    }

@@ -132,14 +167,21 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener {

            Slog.i(TAG, "Display device removed: " + info);
            device.mDebugLastLoggedDeviceInfo = info;
            mListener.onDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
            sendEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
        }
    }

    private void sendEventLocked(DisplayDevice device, int event) {
        final int size = mListeners.size();
        for (int i = 0; i < size; i++) {
            mListeners.get(i).onDisplayDeviceEventLocked(device, event);
        }
    }

    /**
     * Listens to {@link DisplayDevice} events from {@link DisplayDeviceRepository}.
     */
    interface Listener {
    public interface Listener {
        void onDisplayDeviceEventLocked(DisplayDevice device, int event);

        // TODO: multi-display - Try to remove the need for requestTraversal...it feels like
+118 −268

File changed.

Preview size limit exceeded, changes collapsed.

+243 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.os.SystemProperties;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;

import com.android.internal.util.IndentingPrintWriter;

import java.io.PrintWriter;
import java.util.Arrays;
import java.util.function.Consumer;

/**
 * Responsible for creating {@link LogicalDisplay}s and associating them to the
 * {@link DisplayDevice} objects supplied through {@link DisplayAdapter.Listener}.
 *
 * For devices with a single internal display, the mapping is done once and left
 * alone. For devices with multiple built-in displays, such as foldable devices,
 * {@link LogicalDisplay}s can be remapped to different {@link DisplayDevice}s.
 */
class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
    private static final String TAG = "LogicalDisplayMapper";

    public static final int LOGICAL_DISPLAY_EVENT_ADDED = 1;
    public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 2;
    public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 3;

    /**
     * Temporary display info, used for comparing display configurations.
     */
    private final DisplayInfo mTempDisplayInfo = new DisplayInfo();
    private final DisplayInfo mTempNonOverrideDisplayInfo = new DisplayInfo();

    /**
     * True if the display mapper service should pretend there is only one display
     * and only tell applications about the existence of the default logical display.
     * The display manager can still mirror content to secondary displays but applications
     * cannot present unique content on those displays.
     * Used for demonstration purposes only.
     */
    private final boolean mSingleDisplayDemoMode;

    /**
     * List of all logical displays indexed by logical display id.
     * Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
     * TODO: multi-display - Move the aforementioned comment?
     */
    private final SparseArray<LogicalDisplay> mLogicalDisplays =
            new SparseArray<LogicalDisplay>();
    private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;

    private final DisplayDeviceRepository mDisplayDeviceRepo;
    private final PersistentDataStore mPersistentDataStore;
    private final Listener mListener;

    LogicalDisplayMapper(DisplayDeviceRepository repo, Listener listener,
            PersistentDataStore persistentDataStore) {
        mDisplayDeviceRepo = repo;
        mPersistentDataStore = persistentDataStore;
        mListener = listener;
        mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
        mDisplayDeviceRepo.addListener(this);
    }

    @Override
    public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {
        switch (event) {
            case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_ADDED:
                handleDisplayDeviceAddedLocked(device);
                break;

            case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_CHANGED:
                updateLogicalDisplaysLocked();
                break;

            case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_REMOVED:
                updateLogicalDisplaysLocked();
                break;
        }
    }

    @Override
    public void onTraversalRequested() {
        mListener.onTraversalRequested();
    }

    public LogicalDisplay getLocked(int displayId) {
        return mLogicalDisplays.get(displayId);
    }

    public LogicalDisplay getLocked(DisplayDevice device) {
        final int count = mLogicalDisplays.size();
        for (int i = 0; i < count; i++) {
            LogicalDisplay display = mLogicalDisplays.valueAt(i);
            if (display.getPrimaryDisplayDeviceLocked() == device) {
                return display;
            }
        }
        return null;
    }

    public int[] getDisplayIdsLocked(int callingUid) {
        final int count = mLogicalDisplays.size();
        int[] displayIds = new int[count];
        int n = 0;
        for (int i = 0; i < count; i++) {
            LogicalDisplay display = mLogicalDisplays.valueAt(i);
            DisplayInfo info = display.getDisplayInfoLocked();
            if (info.hasAccess(callingUid)) {
                displayIds[n++] = mLogicalDisplays.keyAt(i);
            }
        }
        if (n != count) {
            displayIds = Arrays.copyOfRange(displayIds, 0, n);
        }
        return displayIds;
    }

    public void forEachLocked(Consumer<LogicalDisplay> consumer) {
        final int count = mLogicalDisplays.size();
        for (int i = 0; i < count; i++) {
            consumer.accept(mLogicalDisplays.valueAt(i));
        }
    }

    public void dumpLocked(PrintWriter pw) {
        pw.println("LogicalDisplayMapper:");
        pw.println("  mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
        pw.println("  mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);

        final int logicalDisplayCount = mLogicalDisplays.size();
        pw.println();
        pw.println("  Logical Displays: size=" + logicalDisplayCount);

        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "   ");
        ipw.increaseIndent();

        for (int i = 0; i < logicalDisplayCount; i++) {
            int displayId = mLogicalDisplays.keyAt(i);
            LogicalDisplay display = mLogicalDisplays.valueAt(i);
            pw.println("   Display " + displayId + ":");
            display.dumpLocked(ipw);
        }
    }

    private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
        DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
        boolean isDefault = (deviceInfo.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
        if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) {
            Slog.w(TAG, "Ignoring attempt to add a second default display: " + deviceInfo);
            isDefault = false;
        }

        if (!isDefault && mSingleDisplayDemoMode) {
            Slog.i(TAG, "Not creating a logical display for a secondary display "
                    + " because single display demo mode is enabled: " + deviceInfo);
            return;
        }

        final int displayId = assignDisplayIdLocked(isDefault);
        final int layerStack = assignLayerStackLocked(displayId);

        LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
        display.updateLocked(mDisplayDeviceRepo);
        if (!display.isValidLocked()) {
            // This should never happen currently.
            Slog.w(TAG, "Ignoring display device because the logical display "
                    + "created from it was not considered valid: " + deviceInfo);
            return;
        }

        mLogicalDisplays.put(displayId, display);

        mListener.onLogicalDisplayEventLocked(display,
                LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED);
    }

    /**
     * Updates all existing logical displays given the current set of display devices.
     * Removes invalid logical displays. Sends notifications if needed.
     */
    private void updateLogicalDisplaysLocked() {
        for (int i = mLogicalDisplays.size() - 1; i >= 0; i--) {
            final int displayId = mLogicalDisplays.keyAt(i);
            LogicalDisplay display = mLogicalDisplays.valueAt(i);

            mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
            display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
            display.updateLocked(mDisplayDeviceRepo);
            if (!display.isValidLocked()) {
                mLogicalDisplays.removeAt(i);

                mListener.onLogicalDisplayEventLocked(display,
                        LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED);
            } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
                mListener.onLogicalDisplayEventLocked(display,
                        LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED);
            } else {
                // While applications shouldn't know nor care about the non-overridden info, we
                // still need to let WindowManager know so it can update its own internal state for
                // things like display cutouts.
                display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
                if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
                    mListener.onLogicalDisplayEventLocked(display,
                            LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED);
                }
            }
        }
    }

    private int assignDisplayIdLocked(boolean isDefault) {
        return isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++;
    }

    private int assignLayerStackLocked(int displayId) {
        // Currently layer stacks and display ids are the same.
        // This need not be the case.
        return displayId;
    }

    public interface Listener {
        void onLogicalDisplayEventLocked(LogicalDisplay display, int event);
        void onTraversalRequested();
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -343,7 +343,8 @@ public class DisplayManagerServiceTest {
        displayDeviceInfo2.copyFrom(displayDeviceInfo);
        displayDeviceInfo2.displayCutout = null;
        displayDevice.setDisplayDeviceInfo(displayDeviceInfo2);
        displayManager.handleDisplayDeviceChanged(displayDevice);
        displayManager.getDisplayDeviceRepository()
                .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED);

        handler.runWithScissors(() -> {
        }, 0 /* now */);
+15 −4
Original line number Diff line number Diff line
@@ -28,6 +28,9 @@ import android.view.SurfaceControl;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.io.OutputStream;

public class LogicalDisplayTest {
    private static final int DISPLAY_ID = 0;
    private static final int LAYER_STACK = 0;
@@ -50,13 +53,21 @@ public class LogicalDisplayTest {
        when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(displayDeviceInfo);

        DisplayDeviceRepository repo = new DisplayDeviceRepository(
                new DisplayManagerService.SyncRoot(), new DisplayDeviceRepository.Listener() {
                new DisplayManagerService.SyncRoot(),
                new PersistentDataStore(new PersistentDataStore.Injector() {
                    @Override
                    public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {}
                    public InputStream openRead() {
                        return null;
                    }

                    @Override
                    public OutputStream startWrite() {
                        return null;
                    }

                    @Override
                    public void onTraversalRequested() {}
                });
                    public void finishWrite(OutputStream os, boolean success) {}
                }));
        repo.onDisplayDeviceEvent(mDisplayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
        mLogicalDisplay.updateLocked(repo);
    }
+12 −12

File changed.

Contains only whitespace changes.

Loading