diff options
| author | 2018-08-02 21:16:56 +0000 | |
|---|---|---|
| committer | 2018-08-02 21:16:56 +0000 | |
| commit | c8280ca86a2194eef348e3d837fe78b26b350f81 (patch) | |
| tree | f9e31ddd0d683acb8e7c12535ab34f35929bbb19 | |
| parent | e4c7d37e03146ada9becbc30468a9e4f9ad9c9eb (diff) | |
| parent | e084604e63c0f3ac129b2e2d7d220e3ededa2949 (diff) | |
Merge "Bootstrap freeform external displays."
6 files changed, 288 insertions, 30 deletions
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java index f2b097b28efd..54c2ee51c7a2 100644 --- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java @@ -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); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index d7d5cf84ede0..4ab06a28712d 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1389,9 +1389,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 = diff --git a/services/core/java/com/android/server/wm/DisplaySettings.java b/services/core/java/com/android/server/wm/DisplaySettings.java index 97b64dc2b6b1..bbb690f6a34a 100644 --- a/services/core/java/com/android/server/wm/DisplaySettings.java +++ b/services/core/java/com/android/server/wm/DisplaySettings.java @@ -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"); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index ca4fa648e543..a6bda37d6e3d 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -224,16 +224,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: diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index ea68e17732af..ab735af9a075 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -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; @@ -6910,6 +6912,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); } diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java new file mode 100644 index 000000000000..0ea56ed146fc --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2018 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.wm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.app.WindowConfiguration; +import android.platform.test.annotations.Presubmit; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.Display; +import android.view.DisplayInfo; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class DisplaySettingsTests extends WindowTestsBase { + + private File mTestFolder; + private DisplaySettings mTarget; + + private DisplayContent mPrimaryDisplay; + private DisplayContent mSecondaryDisplay; + + @Before + public void setUp() throws Exception { + super.setUp(); + + mTestFolder = InstrumentationRegistry.getContext().getCacheDir(); + deleteRecursively(mTestFolder); + + sWm.setSupportsFreeformWindowManagement(false); + sWm.setIsPc(false); + + mTarget = new DisplaySettings(sWm, mTestFolder); + mTarget.readSettingsLocked(); + + mPrimaryDisplay = sWm.getDefaultDisplayContentLocked(); + mSecondaryDisplay = createNewDisplay(); + assertNotEquals(Display.DEFAULT_DISPLAY, mSecondaryDisplay.getDisplayId()); + } + + @Test + public void testPrimaryDisplayDefaultToFullscreenWithoutFreeformSupport() { + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, + mPrimaryDisplay.getWindowingMode()); + } + + @Test + public void testPrimaryDisplayDefaultToFullscreenWithFreeformSupportNonPc() { + sWm.setSupportsFreeformWindowManagement(true); + + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, + mPrimaryDisplay.getWindowingMode()); + } + + @Test + public void testPrimaryDisplayDefaultToFreeformWithFreeformIsPc() { + sWm.setSupportsFreeformWindowManagement(true); + sWm.setIsPc(true); + + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM, + mPrimaryDisplay.getWindowingMode()); + } + + @Test + public void testSecondaryDisplayDefaultToFullscreenWithoutFreeformSupport() { + mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, + mSecondaryDisplay.getWindowingMode()); + } + + @Test + public void testSecondaryDisplayDefaultToFreeformWithFreeformSupportNonPc() { + sWm.setSupportsFreeformWindowManagement(true); + + mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM, + mSecondaryDisplay.getWindowingMode()); + } + + @Test + public void testSecondaryDisplayDefaultToFreeformWithFreeformSupportIsPc() { + sWm.setSupportsFreeformWindowManagement(true); + sWm.setIsPc(true); + + mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM, + mSecondaryDisplay.getWindowingMode()); + } + + @Test + public void testDefaultToZeroOverscan() { + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + assertOverscan(mPrimaryDisplay, 0 /* left */, 0 /* top */, 0 /* right */, 0 /* bottom */); + } + + @Test + public void testPersistOverscanInSameInstance() { + final DisplayInfo info = mPrimaryDisplay.getDisplayInfo(); + mTarget.setOverscanLocked(info.uniqueId, info.name, 1 /* left */, 2 /* top */, + 3 /* right */, 4 /* bottom */); + + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */); + } + + @Test + public void testPersistOverscanAcrossInstances() { + final DisplayInfo info = mPrimaryDisplay.getDisplayInfo(); + mTarget.setOverscanLocked(info.uniqueId, info.name, 1 /* left */, 2 /* top */, + 3 /* right */, 4 /* bottom */); + mTarget.writeSettingsLocked(); + + DisplaySettings target = new DisplaySettings(sWm, mTestFolder); + target.readSettingsLocked(); + + target.applySettingsToDisplayLocked(mPrimaryDisplay); + + assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */); + } + + private static void assertOverscan(DisplayContent display, int left, int top, int right, + int bottom) { + final DisplayInfo info = display.getDisplayInfo(); + + assertEquals(left, info.overscanLeft); + assertEquals(top, info.overscanTop); + assertEquals(right, info.overscanRight); + assertEquals(bottom, info.overscanBottom); + } + + private static boolean deleteRecursively(File file) { + if (file.isFile()) { + return file.delete(); + } + + boolean fullyDeleted = true; + final File[] files = file.listFiles(); + for (File child : files) { + fullyDeleted &= deleteRecursively(child); + } + fullyDeleted &= file.delete(); + return fullyDeleted; + } +} |