diff --git a/api/system-current.txt b/api/system-current.txt index 2eaf544a7e749..d50bb929cca34 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -16561,6 +16561,7 @@ package android.hardware.display { method public android.view.Display getDisplay(int); method public android.view.Display[] getDisplays(); method public android.view.Display[] getDisplays(java.lang.String); + method public android.graphics.Point getStableDisplaySize(); method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler); method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener); field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION"; diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 3ade2e430a611..6fbacaf32fd69 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -18,8 +18,10 @@ package android.hardware.display; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; +import android.graphics.Point; import android.media.projection.MediaProjection; import android.os.Handler; import android.util.SparseArray; @@ -585,6 +587,20 @@ public final class DisplayManager { name, width, height, densityDpi, surface, flags, callback, handler, uniqueId); } + /** + * Gets the stable device display size, in pixels. + * + * This should really only be used for things like server-side filtering of available + * applications. Most applications don't need the level of stability guaranteed by this and + * should instead query either the size of the display they're currently running on or the + * size of the default display. + * @hide + */ + @SystemApi + public Point getStableDisplaySize() { + return mGlobal.getStableDisplaySize(); + } + /** * Listens for changes in available display devices. */ diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 0b998e58656b9..a8a4eb67f5809 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -18,6 +18,7 @@ package android.hardware.display; import android.content.Context; import android.content.res.Resources; +import android.graphics.Point; import android.hardware.display.DisplayManager.DisplayListener; import android.media.projection.IMediaProjection; import android.media.projection.MediaProjection; @@ -444,6 +445,17 @@ public final class DisplayManagerGlobal { } } + /** + * Gets the stable device display size, in pixels. + */ + public Point getStableDisplaySize() { + try { + return mDm.getStableDisplaySize(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { @Override public void onDisplayEvent(int displayId, int event) { diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 7ca4dc140eca2..505388419c8c5 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -16,6 +16,7 @@ package android.hardware.display; +import android.graphics.Point; import android.hardware.display.IDisplayManagerCallback; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.WifiDisplay; @@ -77,4 +78,7 @@ interface IDisplayManager { // No permissions required but must be same Uid as the creator. void releaseVirtualDisplay(in IVirtualDisplayCallback token); + + // Get a stable metric for the device's display size. No permissions required. + Point getStableDisplaySize(); } diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ca4b355664d77..172cae97d0a17 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3044,4 +3044,10 @@ true + + + -1 + -1 diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 04cfe485df4a2..4cfc1acc74084 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3069,4 +3069,7 @@ + + + diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 8269042da24a7..d0a1d9e631419 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -31,6 +31,8 @@ import android.Manifest; import android.annotation.NonNull; import android.content.Context; import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.Point; import android.hardware.SensorManager; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; @@ -211,6 +213,12 @@ public final class DisplayManagerService extends SystemService { // The virtual display adapter, or null if not registered. private VirtualDisplayAdapter mVirtualDisplayAdapter; + // The stable device screen height and width. These are not tied to a specific display, even + // the default display, because they need to be stable over the course of the device's entire + // life, even if the default display changes (e.g. a new monitor is plugged into a PC-like + // device). + private Point mStableDisplaySize = new Point(); + // Viewports of the default display and the display that should receive touch // input from an external source. Used by the input system. private final DisplayViewport mDefaultViewport = new DisplayViewport(); @@ -284,7 +292,10 @@ public final class DisplayManagerService extends SystemService { // adapter is up so that we have it's configuration. We could load it lazily, but since // we're going to have to read it in eventually we may as well do it here rather than after // we've waited for the display to register itself with us. - mPersistentDataStore.loadIfNeeded(); + synchronized(mSyncRoot) { + mPersistentDataStore.loadIfNeeded(); + loadStableDisplayValuesLocked(); + } mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS); publishBinderService(Context.DISPLAY_SERVICE, new BinderService(), @@ -346,6 +357,34 @@ public final class DisplayManagerService extends SystemService { return mHandler; } + private void loadStableDisplayValuesLocked() { + final Point size = mPersistentDataStore.getStableDisplaySize(); + if (size.x > 0 && size.y > 0) { + // Just set these values directly so we don't write the display persistent data again + // unnecessarily + mStableDisplaySize.set(size.x, size.y); + } else { + final Resources res = mContext.getResources(); + final int width = res.getInteger( + com.android.internal.R.integer.config_stableDeviceDisplayWidth); + final int height = res.getInteger( + com.android.internal.R.integer.config_stableDeviceDisplayHeight); + if (width > 0 && height > 0) { + setStableDisplaySizeLocked(width, height); + } + } + } + + private Point getStableDisplaySizeInternal() { + Point r = new Point(); + synchronized (mSyncRoot) { + if (mStableDisplaySize.x > 0 && mStableDisplaySize.y > 0) { + r.set(mStableDisplaySize.x, mStableDisplaySize.y); + } + } + return r; + } + private void registerDisplayTransactionListenerInternal( DisplayTransactionListener listener) { // List is self-synchronized copy-on-write. @@ -770,18 +809,6 @@ public final class DisplayManagerService extends SystemService { if (work != null) { work.run(); } - if (display != null && display.getPrimaryDisplayDeviceLocked() == device) { - int colorMode = mPersistentDataStore.getColorMode(device); - if (colorMode == Display.COLOR_MODE_INVALID) { - if ((device.getDisplayDeviceInfoLocked().flags - & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) { - colorMode = mDefaultDisplayDefaultColorMode; - } else { - colorMode = Display.COLOR_MODE_DEFAULT; - } - } - display.setRequestedColorModeLocked(colorMode); - } scheduleTraversalLocked(false); } @@ -886,6 +913,11 @@ public final class DisplayManagerService extends SystemService { return null; } + configureColorModeLocked(display, device); + if (isDefault) { + recordStableDisplayStatsIfNeededLocked(display); + } + mLogicalDisplays.put(displayId, display); // Wake up waitForDefaultDisplay. @@ -907,6 +939,40 @@ public final class DisplayManagerService extends SystemService { return displayId; } + private void configureColorModeLocked(LogicalDisplay display, DisplayDevice device) { + if (display.getPrimaryDisplayDeviceLocked() == device) { + int colorMode = mPersistentDataStore.getColorMode(device); + if (colorMode == Display.COLOR_MODE_INVALID) { + if ((device.getDisplayDeviceInfoLocked().flags + & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) { + colorMode = mDefaultDisplayDefaultColorMode; + } else { + colorMode = Display.COLOR_MODE_DEFAULT; + } + } + display.setRequestedColorModeLocked(colorMode); + } + } + + // If we've never recorded stable device stats for this device before and they aren't + // explicitly configured, go ahead and record the stable device stats now based on the status + // of the default display at first boot. + private void recordStableDisplayStatsIfNeededLocked(LogicalDisplay d) { + if (mStableDisplaySize.x <= 0 && mStableDisplaySize.y <= 0) { + DisplayInfo info = d.getDisplayInfoLocked(); + setStableDisplaySizeLocked(info.getNaturalWidth(), info.getNaturalHeight()); + } + } + + private void setStableDisplaySizeLocked(int width, int height) { + mStableDisplaySize = new Point(width, height); + try { + mPersistentDataStore.setStableDisplaySize(mStableDisplaySize); + } finally { + mPersistentDataStore.saveIfNeeded(); + } + } + // Updates all existing logical displays given the current set of display devices. // Removes invalid logical displays. // Sends notifications if needed. @@ -1166,6 +1232,8 @@ public final class DisplayManagerService extends SystemService { pw.println(" mDefaultDisplayDefaultColorMode=" + mDefaultDisplayDefaultColorMode); pw.println(" mSingleDisplayDemoMode=" + mSingleDisplayDemoMode); pw.println(" mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount); + pw.println(" mStableDisplaySize=" + mStableDisplaySize); + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.increaseIndent(); @@ -1378,6 +1446,19 @@ public final class DisplayManagerService extends SystemService { } } + /** + * Returns the stable device display size, in pixels. + */ + @Override // Binder call + public Point getStableDisplaySize() { + final long token = Binder.clearCallingIdentity(); + try { + return getStableDisplaySizeInternal(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override // Binder call public void registerCallback(IDisplayManagerCallback callback) { if (callback == null) { diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java index 47701b99860af..34c8e22a9f1e0 100644 --- a/services/core/java/com/android/server/display/PersistentDataStore.java +++ b/services/core/java/com/android/server/display/PersistentDataStore.java @@ -23,6 +23,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import android.graphics.Point; import android.hardware.display.WifiDisplay; import android.util.AtomicFile; import android.util.Slog; @@ -60,6 +61,10 @@ import libcore.util.Objects; * <color-mode>0</color-mode> * </display> * </display-states> + * <stable-device-values> + * <stable-display-height>1920<stable-display-height> + * <stable-display-width>1080<stable-display-width> + * </stable-device-values> * </display-manager-state> * * @@ -75,6 +80,9 @@ final class PersistentDataStore { private final HashMap mDisplayStates = new HashMap(); + // Display values which should be stable across the device's lifetime. + private final StableDeviceValues mStableDeviceValues = new StableDeviceValues(); + // The atomic file used to safely read or write the file. private final AtomicFile mAtomicFile; @@ -162,6 +170,7 @@ final class PersistentDataStore { } public boolean forgetWifiDisplay(String deviceAddress) { + loadIfNeeded(); int index = findRememberedWifiDisplay(deviceAddress); if (index >= 0) { mRememberedWifiDisplays.remove(index); @@ -204,6 +213,18 @@ final class PersistentDataStore { return false; } + public Point getStableDisplaySize() { + loadIfNeeded(); + return mStableDeviceValues.getDisplaySize(); + } + + public void setStableDisplaySize(Point size) { + loadIfNeeded(); + if (mStableDeviceValues.setDisplaySize(size)) { + setDirty(); + } + } + private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) { loadIfNeeded(); DisplayState state = mDisplayStates.get(uniqueId); @@ -290,6 +311,9 @@ final class PersistentDataStore { if (parser.getName().equals("display-states")) { loadDisplaysFromXml(parser); } + if (parser.getName().equals("stable-device-values")) { + mStableDeviceValues.loadFromXml(parser); + } } } @@ -363,6 +387,9 @@ final class PersistentDataStore { serializer.endTag(null, "display"); } serializer.endTag(null, "display-states"); + serializer.startTag(null, "stable-device-values"); + mStableDeviceValues.saveToXml(serializer); + serializer.endTag(null, "stable-device-values"); serializer.endTag(null, "display-manager-state"); serializer.endDocument(); } @@ -382,6 +409,8 @@ final class PersistentDataStore { pw.println(" " + i++ + ": " + entry.getKey()); entry.getValue().dump(pw, " "); } + pw.println(" StableDeviceValues:"); + mStableDeviceValues.dump(pw, " "); } private static final class DisplayState { @@ -417,8 +446,66 @@ final class PersistentDataStore { serializer.endTag(null, "color-mode"); } - private void dump(final PrintWriter pw, final String prefix) { + public void dump(final PrintWriter pw, final String prefix) { pw.println(prefix + "ColorMode=" + mColorMode); } } + + private static final class StableDeviceValues { + private int mWidth; + private int mHeight; + + private Point getDisplaySize() { + return new Point(mWidth, mHeight); + } + + public boolean setDisplaySize(Point r) { + if (mWidth != r.x || mHeight != r.y) { + mWidth = r.x; + mHeight = r.y; + return true; + } + return false; + } + + public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + switch (parser.getName()) { + case "stable-display-width": + mWidth = loadIntValue(parser); + break; + case "stable-display-height": + mHeight = loadIntValue(parser); + break; + } + } + } + + private static int loadIntValue(XmlPullParser parser) + throws IOException, XmlPullParserException { + try { + String value = parser.nextText(); + return Integer.parseInt(value); + } catch (NumberFormatException nfe) { + return 0; + } + } + + public void saveToXml(XmlSerializer serializer) throws IOException { + if (mWidth > 0 && mHeight > 0) { + serializer.startTag(null, "stable-display-width"); + serializer.text(Integer.toString(mWidth)); + serializer.endTag(null, "stable-display-width"); + serializer.startTag(null, "stable-display-height"); + serializer.text(Integer.toString(mHeight)); + serializer.endTag(null, "stable-display-height"); + } + } + + public void dump(final PrintWriter pw, final String prefix) { + pw.println(prefix + "StableDisplayWidth=" + mWidth); + pw.println(prefix + "StableDisplayHeight=" + mHeight); + } + } }