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

Commit e084604e authored by Garfield Tan's avatar Garfield Tan
Browse files

Bootstrap freeform external displays.

To get freeform external display one can:
1. Enable freeform windows in developer options;
2. Reboot the device;
3. Connect an external device.

All tasks launched in the external display are then launched in freeform
mode by default, and centered in that display.

There are a lot to do after this CL. It needs caption and window decor
so it can be maximized, restored, drag-moved and drag-resized. There is
no system UI on external display yet so we can't launch arbitrary
activities on it, nor can we minimize anything.

I'm using ActivityView for testing and it uses input forwarder, but we
need to solve the use of mouse and keyboard when it goes to a real
external display.

In addition, tightened the visibility of DisplaySettings class.

Bug: 111840884
Test: go/wm-smoke
Test: activity launching on external freeform displays.
Test: atest DisplaySettingsTests
Change-Id: Ie2a05110ada60b054ac35dae948ad309ea378b1c
parent 3b388800
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static android.Manifest.permission.REMOVE_TASKS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.STOP_APP_SWITCHES;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
import static android.provider.Settings.System.FONT_SCALE;
@@ -541,6 +542,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
        final boolean forceResizable = Settings.Global.getInt(
                resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
        final boolean isPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);

        // Transfer any global setting for forcing RTL layout, into a System Property
        SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
@@ -573,6 +575,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
            }
            mWindowManager.setForceResizableTasks(mForceResizableActivities);
            mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
            mWindowManager.setSupportsFreeformWindowManagement(mSupportsFreeformWindowManagement);
            mWindowManager.setIsPc(isPc);
            // This happens before any activities are started, so we can change global configuration
            // in-place.
            updateConfigurationLocked(configuration, null, true);
+1 −3
Original line number Diff line number Diff line
@@ -1318,9 +1318,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        final int dw = displayInfo.logicalWidth;
        final int dh = displayInfo.logicalHeight;
        config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
        // TODO: Probably best to set this based on some setting in the display content object,
        // so the display can be configured for things like fullscreen.
        config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
        config.windowConfiguration.setWindowingMode(getWindowingMode());

        final float density = mDisplayMetrics.density;
        config.screenWidthDp =
+85 −18
Original line number Diff line number Diff line
@@ -19,12 +19,19 @@ package com.android.server.wm;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

import android.app.WindowConfiguration;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Environment;
import android.provider.Settings;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
import android.view.Display;
import android.view.DisplayInfo;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;

@@ -43,37 +50,48 @@ import org.xmlpull.v1.XmlSerializer;
/**
 * Current persistent settings about a display
 */
public class DisplaySettings {
class DisplaySettings {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplaySettings" : TAG_WM;

    private final WindowManagerService mService;
    private final AtomicFile mFile;
    private final HashMap<String, Entry> mEntries = new HashMap<String, Entry>();

    public static class Entry {
        public final String name;
        public int overscanLeft;
        public int overscanTop;
        public int overscanRight;
        public int overscanBottom;
    private static class Entry {
        private final String name;
        private int overscanLeft;
        private int overscanTop;
        private int overscanRight;
        private int overscanBottom;
        private int windowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;

        public Entry(String _name) {
        private Entry(String _name) {
            name = _name;
        }
    }

    public DisplaySettings() {
        File dataDir = Environment.getDataDirectory();
        File systemDir = new File(dataDir, "system");
        mFile = new AtomicFile(new File(systemDir, "display_settings.xml"), "wm-displays");
    DisplaySettings(WindowManagerService service) {
        this(service, new File(Environment.getDataDirectory(), "system"));
    }

    public void getOverscanLocked(String name, String uniqueId, Rect outRect) {
    @VisibleForTesting
    DisplaySettings(WindowManagerService service, File folder) {
        mService = service;
        mFile = new AtomicFile(new File(folder, "display_settings.xml"), "wm-displays");
    }

    private Entry getEntry(String name, String uniqueId) {
        // Try to get the entry with the unique if possible.
        // Else, fall back on the display name.
        Entry entry;
        if (uniqueId == null || (entry = mEntries.get(uniqueId)) == null) {
            entry = mEntries.get(name);
        }
        return entry;
    }

    private void getOverscanLocked(String name, String uniqueId, Rect outRect) {
        final Entry entry = getEntry(name, uniqueId);
        if (entry != null) {
            outRect.left = entry.overscanLeft;
            outRect.top = entry.overscanTop;
@@ -84,7 +102,7 @@ public class DisplaySettings {
        }
    }

    public void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
    void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
            int bottom) {
        if (left == 0 && top == 0 && right == 0 && bottom == 0) {
            // Right now all we are storing is overscan; if there is no overscan,
@@ -105,7 +123,47 @@ public class DisplaySettings {
        entry.overscanBottom = bottom;
    }

    public void readSettingsLocked() {
    private int getWindowingModeLocked(String name, String uniqueId, int displayId) {
        final Entry entry = getEntry(name, uniqueId);
        int windowingMode = entry != null ? entry.windowingMode
                : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
        // This display used to be in freeform, but we don't support freeform anymore, so fall
        // back to fullscreen.
        if (windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
                && !mService.mSupportsFreeformWindowManagement) {
            return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
        }
        // No record is present so use default windowing mode policy.
        if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
            if (displayId == Display.DEFAULT_DISPLAY) {
                windowingMode = (mService.mIsPc && mService.mSupportsFreeformWindowManagement)
                        ? WindowConfiguration.WINDOWING_MODE_FREEFORM
                        : WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
            } else {
                windowingMode = mService.mSupportsFreeformWindowManagement
                        ? WindowConfiguration.WINDOWING_MODE_FREEFORM
                        : WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
            }
        }
        return windowingMode;
    }

    void applySettingsToDisplayLocked(DisplayContent dc) {
        final DisplayInfo displayInfo = dc.getDisplayInfo();

        // Setting windowing mode first, because it may override overscan values later.
        dc.setWindowingMode(getWindowingModeLocked(displayInfo.name, displayInfo.uniqueId,
                dc.getDisplayId()));

        final Rect rect = new Rect();
        getOverscanLocked(displayInfo.name, displayInfo.uniqueId, rect);
        displayInfo.overscanLeft = rect.left;
        displayInfo.overscanTop = rect.top;
        displayInfo.overscanRight = rect.right;
        displayInfo.overscanBottom = rect.bottom;
    }

    void readSettingsLocked() {
        FileInputStream stream;
        try {
            stream = mFile.openRead();
@@ -169,11 +227,15 @@ public class DisplaySettings {
    }

    private int getIntAttribute(XmlPullParser parser, String name) {
        return getIntAttribute(parser, name, 0 /* defaultValue */);
    }

    private int getIntAttribute(XmlPullParser parser, String name, int defaultValue) {
        try {
            String str = parser.getAttributeValue(null, name);
            return str != null ? Integer.parseInt(str) : 0;
            return str != null ? Integer.parseInt(str) : defaultValue;
        } catch (NumberFormatException e) {
            return 0;
            return defaultValue;
        }
    }

@@ -186,12 +248,14 @@ public class DisplaySettings {
            entry.overscanTop = getIntAttribute(parser, "overscanTop");
            entry.overscanRight = getIntAttribute(parser, "overscanRight");
            entry.overscanBottom = getIntAttribute(parser, "overscanBottom");
            entry.windowingMode = getIntAttribute(parser, "windowingMode",
                    WindowConfiguration.WINDOWING_MODE_UNDEFINED);
            mEntries.put(name, entry);
        }
        XmlUtils.skipCurrentTag(parser);
    }

    public void writeSettingsLocked() {
    void writeSettingsLocked() {
        FileOutputStream stream;
        try {
            stream = mFile.startWrite();
@@ -221,6 +285,9 @@ public class DisplaySettings {
                if (entry.overscanBottom != 0) {
                    out.attribute(null, "overscanBottom", Integer.toString(entry.overscanBottom));
                }
                if (entry.windowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
                    out.attribute(null, "windowingMode", Integer.toString(entry.windowingMode));
                }
                out.endTag(null, "display");
            }

+3 −8
Original line number Diff line number Diff line
@@ -221,16 +221,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {

        if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);

        final DisplayInfo displayInfo = dc.getDisplayInfo();
        final Rect rect = new Rect();
        mService.mDisplaySettings.getOverscanLocked(displayInfo.name, displayInfo.uniqueId, rect);
        displayInfo.overscanLeft = rect.left;
        displayInfo.overscanTop = rect.top;
        displayInfo.overscanRight = rect.right;
        displayInfo.overscanBottom = rect.bottom;
        mService.mDisplaySettings.applySettingsToDisplayLocked(dc);

        if (mService.mDisplayManagerInternal != null) {
            mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
                    displayId, displayInfo);
                    displayId, dc.getDisplayInfo());
            dc.configureDisplayPolicy();

            // Tap Listeners are supported for:
+15 −1
Original line number Diff line number Diff line
@@ -565,6 +565,8 @@ public class WindowManagerService extends IWindowManager.Stub

    boolean mForceResizableTasks = false;
    boolean mSupportsPictureInPicture = false;
    boolean mSupportsFreeformWindowManagement = false;
    boolean mIsPc = false;

    boolean mDisableTransitionAnimation = false;

@@ -953,7 +955,7 @@ public class WindowManagerService extends IWindowManager.Stub
                com.android.internal.R.bool.config_disableTransitionAnimation);
        mInputManager = inputManager; // Must be before createDisplayContentLocked.
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
        mDisplaySettings = new DisplaySettings();
        mDisplaySettings = new DisplaySettings(this);
        mDisplaySettings.readSettingsLocked();

        mPolicy = policy;
@@ -6922,6 +6924,18 @@ public class WindowManagerService extends IWindowManager.Stub
        }
    }

    public void setSupportsFreeformWindowManagement(boolean supportsFreeformWindowManagement) {
        synchronized (mWindowMap) {
            mSupportsFreeformWindowManagement = supportsFreeformWindowManagement;
        }
    }

    public void setIsPc(boolean isPc) {
        synchronized (mWindowMap) {
            mIsPc = isPc;
        }
    }

    static int dipToPixel(int dip, DisplayMetrics displayMetrics) {
        return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
    }
Loading