Add minimal post processing API to framework
This API allows applications to instruct the connected display to do minimal
post processing on the produced image or video frames. This will switch the
display to a low latency mode (ALLM, Game mode or some other custom
implementation thereof), reducing lag in the final images. Thus, minimal post
processing would greatly enhance performance for gaming and video
conferencing applications. It would not, however, suit applications that
prioritise image quality over performance.
This CL adds 2 public method:
- Window.setPreferMinimalPostProcessing()
(this can also be set in WindowManager.LayoutParams.preferMinimalPostProcessing)
If minimal post processing is preferred, the connected display will be requested
to go into low latency mode, which reduces image processing, resulting in better
performance for gaming applications. If the Display sink is connected via HDMI,
the device will begin to send infoframes with Auto Low Latency Mode enabled and
Game Content Type. This will switch the connected display to a lower latency
mode (if available).
For more information, see HDMI 2.1 specification.
If the Display sink has an internal connection or uses some other protocol than
HDMI, effects may be similar but implementation-defined.
- Display.isMinimalPostProcessingPreferred()
Returns true if the connected display supports either Low Latency Mode (ALLM or
some other custom low latency implementation) or Game content type.
Bug: 135116095
Test: make -> flash on ATV OTT device -> open an activity which requests minimal
post processing -> check SurfaceControl logs -> verify correct signals are
passed to native
Change-Id: Id09160ba1513fef4dac979162bcda3bfeaace0e6
diff --git a/api/current.txt b/api/current.txt
index 1728c53..a8b8c2e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1059,6 +1059,7 @@
field public static final int popupWindowStyle = 16842870; // 0x1010076
field public static final int port = 16842793; // 0x1010029
field public static final int positiveButtonText = 16843253; // 0x10101f5
+ field public static final int preferMinimalPostProcessing = 16844300; // 0x101060c
field public static final int preferenceCategoryStyle = 16842892; // 0x101008c
field public static final int preferenceFragmentStyle = 16844038; // 0x1010506
field public static final int preferenceInformationStyle = 16842893; // 0x101008d
@@ -11174,6 +11175,7 @@
field public String parentActivityName;
field public String permission;
field public int persistableMode;
+ field public boolean preferMinimalPostProcessing;
field public int screenOrientation;
field public int softInputMode;
field public String targetActivity;
@@ -49125,6 +49127,7 @@
method @Deprecated public float[] getSupportedRefreshRates();
method @Deprecated public int getWidth();
method public boolean isHdr();
+ method public boolean isMinimalPostProcessingSupported();
method public boolean isValid();
method public boolean isWideColorGamut();
field public static final int DEFAULT_DISPLAY = 0; // 0x0
@@ -52150,6 +52153,7 @@
method public abstract void setNavigationBarColor(@ColorInt int);
method public void setNavigationBarContrastEnforced(boolean);
method public void setNavigationBarDividerColor(@ColorInt int);
+ method public void setPreferMinimalPostProcessing(boolean);
method public void setReenterTransition(android.transition.Transition);
method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
method public final void setRestrictedCaptionAreaListener(android.view.Window.OnRestrictedCaptionAreaChangedListener);
@@ -52467,6 +52471,7 @@
field public int layoutInDisplayCutoutMode;
field @Deprecated public int memoryType;
field public String packageName;
+ field public boolean preferMinimalPostProcessing;
field public int preferredDisplayModeId;
field @Deprecated public float preferredRefreshRate;
field public int rotationAnimation;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f54e841..ba721ce 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7853,6 +7853,7 @@
mCurrentConfig = config;
mWindow.setColorMode(info.colorMode);
+ mWindow.setPreferMinimalPostProcessing(info.preferMinimalPostProcessing);
setAutofillOptions(application.getAutofillOptions());
setContentCaptureOptions(application.getContentCaptureOptions());
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index e724443..1ec51b5 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -290,6 +290,34 @@
public int colorMode = COLOR_MODE_DEFAULT;
/**
+ * Indicates whether the activity wants the connected display to do minimal post processing on
+ * the produced image or video frames. This will only be requested if this activity's main
+ * window is visible on the screen.
+ *
+ * <p>This setting should be used when low latency has a higher priority than image enhancement
+ * processing (e.g. for games or video conferencing).
+ *
+ * <p>If the Display sink is connected via HDMI, the device will begin to send infoframes with
+ * Auto Low Latency Mode enabled and Game Content Type. This will switch the connected display
+ * to a minimal image processing mode (if available), which reduces latency, improving the user
+ * experience for gaming or video conferencing applications. For more information, see HDMI 2.1
+ * specification.
+ *
+ * <p>If the Display sink has an internal connection or uses some other protocol than HDMI,
+ * effects may be similar but implementation-defined.
+ *
+ * <p>The ability to switch to a mode with minimal post proessing may be disabled by a user
+ * setting in the system settings menu. In that case, this field is ignored and the display will
+ * remain in its current mode.
+ *
+ * <p>Set from attribute {@link android.R.attr#preferMinimalPostProcessing}.
+ *
+ * @see android.view.WindowManager.LayoutParams#preferMinimalPostProcessing
+ * @see android.view.Display#isMinimalPostProcessingSupported
+ */
+ public boolean preferMinimalPostProcessing = false;
+
+ /**
* Bit in {@link #flags} indicating whether this activity is able to
* run in multiple processes. If
* true, the system may instantiate it in the some process as the
@@ -1004,6 +1032,7 @@
requestedVrComponent = orig.requestedVrComponent;
rotationAnimation = orig.rotationAnimation;
colorMode = orig.colorMode;
+ preferMinimalPostProcessing = orig.preferMinimalPostProcessing;
maxAspectRatio = orig.maxAspectRatio;
minAspectRatio = orig.minAspectRatio;
}
@@ -1231,6 +1260,7 @@
dest.writeInt(colorMode);
dest.writeFloat(maxAspectRatio);
dest.writeFloat(minAspectRatio);
+ dest.writeBoolean(preferMinimalPostProcessing);
}
/**
@@ -1349,6 +1379,7 @@
colorMode = source.readInt();
maxAspectRatio = source.readFloat();
minAspectRatio = source.readFloat();
+ preferMinimalPostProcessing = source.readBoolean();
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index c955137..df2d0dd 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -24,7 +24,6 @@
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
-import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -151,11 +150,14 @@
* has a preference.
* @param requestedModeId The preferred mode id for the top-most visible window that has a
* preference.
+ * @param preferMinimalPostProcessing Whether there is a visible window on the screen that wants
+ * minimal post processing.
* @param inTraversal True if called from WindowManagerService during a window traversal
* prior to call to performTraversalInTransactionFromWindowManager.
*/
public abstract void setDisplayProperties(int displayId, boolean hasContent,
- float requestedRefreshRate, int requestedModeId, boolean inTraversal);
+ float requestedRefreshRate, int requestedModeId, boolean preferMinimalPostProcessing,
+ boolean inTraversal);
/**
* Applies an offset to the contents of a display, for example to avoid burn-in.
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 03e68b0..65d5190 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.KeyguardManager;
@@ -857,6 +858,31 @@
}
/**
+ * <p> Returns true if the connected display can be switched into a mode with minimal
+ * post processing. </p>
+ *
+ * <p> If the Display sink is connected via HDMI, this method will return true if the
+ * display supports either Auto Low Latency Mode or Game Content Type.
+ *
+ * <p> If the Display sink has an internal connection or uses some other protocol than
+ * HDMI, this method will return true if the sink can be switched into an
+ * implementation-defined low latency image processing mode. </p>
+ *
+ * <p> The ability to switch to a mode with minimal post processing may be disabled
+ * by a user setting in the system settings menu. In that case, this method returns
+ * false. </p>
+ *
+ * @see android.view.Window#setPreferMinimalPostProcessing
+ */
+ @SuppressLint("VisiblySynchronized")
+ public boolean isMinimalPostProcessingSupported() {
+ synchronized (this) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.minimalPostProcessingSupported;
+ }
+ }
+
+ /**
* Request the display applies a color mode.
* @hide
*/
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 38baccb..ceeccac 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -184,6 +184,14 @@
public Display.HdrCapabilities hdrCapabilities;
/**
+ * Indicates whether the display can be switched into a mode with minimal post
+ * processing.
+ *
+ * @see android.view.Display#isMinimalPostProcessingSupported
+ */
+ public boolean minimalPostProcessingSupported;
+
+ /**
* The logical display density which is the basis for density-independent
* pixels.
*/
@@ -305,6 +313,7 @@
&& colorMode == other.colorMode
&& Arrays.equals(supportedColorModes, other.supportedColorModes)
&& Objects.equals(hdrCapabilities, other.hdrCapabilities)
+ && minimalPostProcessingSupported == other.minimalPostProcessingSupported
&& logicalDensityDpi == other.logicalDensityDpi
&& physicalXDpi == other.physicalXDpi
&& physicalYDpi == other.physicalYDpi
@@ -346,6 +355,7 @@
supportedColorModes = Arrays.copyOf(
other.supportedColorModes, other.supportedColorModes.length);
hdrCapabilities = other.hdrCapabilities;
+ minimalPostProcessingSupported = other.minimalPostProcessingSupported;
logicalDensityDpi = other.logicalDensityDpi;
physicalXDpi = other.physicalXDpi;
physicalYDpi = other.physicalYDpi;
@@ -388,6 +398,7 @@
supportedColorModes[i] = source.readInt();
}
hdrCapabilities = source.readParcelable(null);
+ minimalPostProcessingSupported = source.readBoolean();
logicalDensityDpi = source.readInt();
physicalXDpi = source.readFloat();
physicalYDpi = source.readFloat();
@@ -430,6 +441,7 @@
dest.writeInt(supportedColorModes[i]);
}
dest.writeParcelable(hdrCapabilities, flags);
+ dest.writeBoolean(minimalPostProcessingSupported);
dest.writeInt(logicalDensityDpi);
dest.writeFloat(physicalXDpi);
dest.writeFloat(physicalYDpi);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 6458737..527b38cc 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -168,6 +168,8 @@
private static native int nativeGetActiveColorMode(IBinder displayToken);
private static native boolean nativeSetActiveColorMode(IBinder displayToken,
int colorMode);
+ private static native void nativeSetAutoLowLatencyMode(IBinder displayToken, boolean on);
+ private static native void nativeSetGameContentType(IBinder displayToken, boolean on);
private static native void nativeSetDisplayPowerMode(
IBinder displayToken, int mode);
private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject,
@@ -185,6 +187,9 @@
private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken);
+ private static native boolean nativeGetAutoLowLatencyModeSupport(IBinder displayToken);
+ private static native boolean nativeGetGameContentTypeSupport(IBinder displayToken);
+
private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject,
InputWindowHandle handle);
@@ -1618,6 +1623,28 @@
/**
* @hide
*/
+ public static void setAutoLowLatencyMode(IBinder displayToken, boolean on) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ nativeSetAutoLowLatencyMode(displayToken, on);
+ }
+
+ /**
+ * @hide
+ */
+ public static void setGameContentType(IBinder displayToken, boolean on) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ nativeSetGameContentType(displayToken, on);
+ }
+
+ /**
+ * @hide
+ */
@UnsupportedAppUsage
public static void setDisplayProjection(IBinder displayToken,
int orientation, Rect layerStackRect, Rect displayRect) {
@@ -1669,6 +1696,28 @@
/**
* @hide
*/
+ public static boolean getAutoLowLatencyModeSupport(IBinder displayToken) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ return nativeGetAutoLowLatencyModeSupport(displayToken);
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean getGameContentTypeSupport(IBinder displayToken) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ return nativeGetGameContentTypeSupport(displayToken);
+ }
+
+ /**
+ * @hide
+ */
@UnsupportedAppUsage
public static IBinder createDisplay(String name, boolean secure) {
if (name == null) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index c699cdc..df3590f 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1207,6 +1207,44 @@
}
/**
+ * If {@code isPreferred} is true, this method requests that the connected display does minimal
+ * post processing when this window is visible on the screen. Otherwise, it requests that the
+ * display switches back to standard image processing.
+ *
+ * <p> By default, the display does not do minimal post processing and if this is desired, this
+ * method should not be used. It should be used with {@code isPreferred=true} when low
+ * latency has a higher priority than image enhancement processing (e.g. for games or video
+ * conferencing). The display will automatically go back into standard image processing mode
+ * when no window requesting minimal posst processing is visible on screen anymore.
+ * {@code setPreferMinimalPostProcessing(false)} can be used if
+ * {@code setPreferMinimalPostProcessing(true)} was previously called for this window and
+ * minimal post processing is no longer required.
+ *
+ * <p>If the Display sink is connected via HDMI, the device will begin to send infoframes with
+ * Auto Low Latency Mode enabled and Game Content Type. This will switch the connected display
+ * to a minimal image processing mode (if available), which reduces latency, improving the user
+ * experience for gaming or video conferencing applications. For more information, see HDMI 2.1
+ * specification.
+ *
+ * <p>If the Display sink has an internal connection or uses some other protocol than HDMI,
+ * effects may be similar but implementation-defined.
+ *
+ * <p>The ability to switch to a mode with minimal post proessing may be disabled by a user
+ * setting in the system settings menu. In that case, this method does nothing.
+ *
+ * @see android.content.pm.ActivityInfo#preferMinimalPostProcessing
+ * @see android.view.Display#isMinimalPostProcessingSupported
+ * @see android.view.WindowManager.LayoutParams#preferMinimalPostProcessing
+ *
+ * @param isPreferred Indicates whether minimal post processing is preferred for this window
+ * ({@code isPreferred=true}) or not ({@code isPreferred=false}).
+ */
+ public void setPreferMinimalPostProcessing(boolean isPreferred) {
+ mWindowAttributes.preferMinimalPostProcessing = isPreferred;
+ dispatchWindowAttributesChanged(mWindowAttributes);
+ }
+
+ /**
* Returns the requested color mode of the window, one of
* {@link ActivityInfo#COLOR_MODE_DEFAULT}, {@link ActivityInfo#COLOR_MODE_WIDE_COLOR_GAMUT}
* or {@link ActivityInfo#COLOR_MODE_HDR}. If {@link ActivityInfo#COLOR_MODE_WIDE_COLOR_GAMUT}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c62e69c..2f8a99e 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2601,6 +2601,33 @@
public long hideTimeoutMilliseconds = -1;
/**
+ * Indicates whether this window wants the connected display to do minimal post processing
+ * on the produced image or video frames. This will only be requested if the window is
+ * visible on the screen.
+ *
+ * <p>This setting should be used when low latency has a higher priority than image
+ * enhancement processing (e.g. for games or video conferencing).
+ *
+ * <p>If the Display sink is connected via HDMI, the device will begin to send infoframes
+ * with Auto Low Latency Mode enabled and Game Content Type. This will switch the connected
+ * display to a minimal image processing mode (if available), which reduces latency,
+ * improving the user experience for gaming or video conferencing applications. For more
+ * information, see HDMI 2.1 specification.
+ *
+ * <p>If the Display sink has an internal connection or uses some other protocol than HDMI,
+ * effects may be similar but implementation-defined.
+ *
+ * <p>The ability to switch to a mode with minimal post proessing may be disabled by a user
+ * setting in the system settings menu. In that case, this field is ignored and the display
+ * will remain in its current mode.
+ *
+ * @see android.content.pm.ActivityInfo#preferMinimalPostProcessing
+ * @see android.view.Display#isMinimalPostProcessingSupported
+ * @see android.view.Window#setPreferMinimalPostProcessing
+ */
+ public boolean preferMinimalPostProcessing = false;
+
+ /**
* The color mode requested by this window. The target display may
* not be able to honor the request. When the color mode is not set
* to {@link ActivityInfo#COLOR_MODE_DEFAULT}, it might override the
@@ -2783,6 +2810,7 @@
out.writeLong(hideTimeoutMilliseconds);
out.writeInt(insetsFlags.appearance);
out.writeInt(insetsFlags.behavior);
+ out.writeBoolean(preferMinimalPostProcessing);
}
public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR
@@ -2841,6 +2869,7 @@
hideTimeoutMilliseconds = in.readLong();
insetsFlags.appearance = in.readInt();
insetsFlags.behavior = in.readInt();
+ preferMinimalPostProcessing = in.readBoolean();
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -2888,6 +2917,8 @@
public static final int COLOR_MODE_CHANGED = 1 << 26;
/** {@hide} */
public static final int INSET_FLAGS_CHANGED = 1 << 27;
+ /** {@hide} */
+ public static final int MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED = 1 << 28;
// internal buffer to backup/restore parameters under compatibility mode.
private int[] mCompatibilityParamsBackup = null;
@@ -3073,6 +3104,11 @@
changes |= COLOR_MODE_CHANGED;
}
+ if (preferMinimalPostProcessing != o.preferMinimalPostProcessing) {
+ preferMinimalPostProcessing = o.preferMinimalPostProcessing;
+ changes |= MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED;
+ }
+
// This can't change, it's only set at window creation time.
hideTimeoutMilliseconds = o.hideTimeoutMilliseconds;
@@ -3215,6 +3251,10 @@
if (mColorMode != COLOR_MODE_DEFAULT) {
sb.append(" colorMode=").append(ActivityInfo.colorModeToString(mColorMode));
}
+ if (preferMinimalPostProcessing) {
+ sb.append(" preferMinimalPostProcessing=");
+ sb.append(preferMinimalPostProcessing);
+ }
sb.append(System.lineSeparator());
sb.append(prefix).append(" fl=").append(
ViewDebug.flagsToString(LayoutParams.class, "flags", flags));
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 9f20388..8d82bc7 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1174,6 +1174,34 @@
capabilities.getDesiredMaxAverageLuminance(), capabilities.getDesiredMinLuminance());
}
+static jboolean nativeGetAutoLowLatencyModeSupport(JNIEnv* env, jclass clazz, jobject tokenObject) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+ if (token == NULL) return NULL;
+
+ return SurfaceComposerClient::getAutoLowLatencyModeSupport(token);
+}
+
+static jboolean nativeGetGameContentTypeSupport(JNIEnv* env, jclass clazz, jobject tokenObject) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+ if (token == NULL) return NULL;
+
+ return SurfaceComposerClient::getGameContentTypeSupport(token);
+}
+
+static void nativeSetAutoLowLatencyMode(JNIEnv* env, jclass clazz, jobject tokenObject, jboolean on) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+ if (token == NULL) return;
+
+ SurfaceComposerClient::setAutoLowLatencyMode(token, on);
+}
+
+static void nativeSetGameContentType(JNIEnv* env, jclass clazz, jobject tokenObject, jboolean on) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+ if (token == NULL) return;
+
+ SurfaceComposerClient::setGameContentType(token, on);
+}
+
static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
if (parcel == NULL) {
@@ -1374,6 +1402,14 @@
(void*)nativeGetActiveColorMode},
{"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z",
(void*)nativeSetActiveColorMode},
+ {"nativeGetAutoLowLatencyModeSupport", "(Landroid/os/IBinder;)Z",
+ (void*)nativeGetAutoLowLatencyModeSupport },
+ {"nativeSetAutoLowLatencyMode", "(Landroid/os/IBinder;Z)V",
+ (void*)nativeSetAutoLowLatencyMode },
+ {"nativeGetGameContentTypeSupport", "(Landroid/os/IBinder;)Z",
+ (void*)nativeGetGameContentTypeSupport },
+ {"nativeSetGameContentType", "(Landroid/os/IBinder;Z)V",
+ (void*)nativeSetGameContentType },
{"nativeGetCompositionDataspaces", "()[I",
(void*)nativeGetCompositionDataspaces},
{"nativeGetHdrCapabilities", "(Landroid/os/IBinder;)Landroid/view/Display$HdrCapabilities;",
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index ffcfe43..cae2faa 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2497,6 +2497,30 @@
<enum name="hdr" value="2" />
</attr>
<attr name="forceQueryable" format="boolean" />
+ <!-- Indicates whether the activity wants the connected display to do minimal
+ post processing on the produced image or video frames. This will only be
+ requested if this activity's main window is visible on the screen.
+
+ <p> This setting should be used when low latency has a higher priority than
+ image enhancement processing (e.g. for games or video conferencing).
+
+ <p> If the Display sink is connected via HDMI, the device will begin to
+ send infoframes with Auto Low Latency Mode enabled and Game Content Type.
+ This will switch the connected display to a minimal image processing mode
+ (if available), which reduces latency, improving the user experience for
+ gaming or video conferencing applications. For more information,
+ see HDMI 2.1 specification.
+
+ <p> If the Display sink has an internal connection or uses some other
+ protocol than HDMI, effects may be similar but implementation-defined.
+
+ <p> The ability to switch to a mode with minimal post proessing may be
+ disabled by a user setting in the system settings menu. In that case,
+ this field is ignored and the display will remain in its current
+ mode.
+
+ <p> See {@link android.content.pm.ActivityInfo #preferMinimalPostProcessing} -->
+ <attr name="preferMinimalPostProcessing" format="boolean"/>
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index cde95aa..1acbb35 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3048,6 +3048,10 @@
<public name="accessibilitySystemActionLockScreen" />
<public name="accessibilitySystemActionTakeScreenshot" />
</public-group>
+
+ <public-group type="attr" first-id="0x0101060c">
+ <public name="preferMinimalPostProcessing"/>
+ </public-group>
<!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 9882f6c..eb63c12 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -153,6 +153,24 @@
public void setRequestedColorModeLocked(int colorMode) {
}
+ /**
+ * Sends the Auto Low Latency Mode (ALLM) signal over HDMI, or requests an internal display to
+ * switch to a low-latency mode.
+ *
+ * @param on Whether to set ALLM on or off.
+ */
+ public void setAutoLowLatencyModeLocked(boolean on) {
+ }
+
+ /**
+ * Sends a ContentType=Game signal over HDMI, or requests an internal display to switch to a
+ * game mode (generally lower latency).
+ *
+ * @param on Whether to send a ContentType=Game signal or not
+ */
+ public void setGameContentTypeLocked(boolean on) {
+ }
+
public void onOverlayChangedLocked() {
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 729ea17..ac41434 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -207,6 +207,16 @@
public Display.HdrCapabilities hdrCapabilities;
/**
+ * Indicates whether this display supports Auto Low Latency Mode.
+ */
+ public boolean allmSupported;
+
+ /**
+ * Indicates whether this display suppors Game content type.
+ */
+ public boolean gameContentTypeSupported;
+
+ /**
* The nominal apparent density of the display in DPI used for layout calculations.
* This density is sensitive to the viewing distance. A big TV and a tablet may have
* the same apparent density even though the pixels on the TV are much bigger than
@@ -337,6 +347,8 @@
|| !Arrays.equals(supportedModes, other.supportedModes)
|| !Arrays.equals(supportedColorModes, other.supportedColorModes)
|| !Objects.equals(hdrCapabilities, other.hdrCapabilities)
+ || allmSupported != other.allmSupported
+ || gameContentTypeSupported != other.gameContentTypeSupported
|| densityDpi != other.densityDpi
|| xDpi != other.xDpi
|| yDpi != other.yDpi
@@ -371,6 +383,8 @@
colorMode = other.colorMode;
supportedColorModes = other.supportedColorModes;
hdrCapabilities = other.hdrCapabilities;
+ allmSupported = other.allmSupported;
+ gameContentTypeSupported = other.gameContentTypeSupported;
densityDpi = other.densityDpi;
xDpi = other.xDpi;
yDpi = other.yDpi;
@@ -400,6 +414,8 @@
sb.append(", colorMode ").append(colorMode);
sb.append(", supportedColorModes ").append(Arrays.toString(supportedColorModes));
sb.append(", HdrCapabilities ").append(hdrCapabilities);
+ sb.append(", allmSupported ").append(allmSupported);
+ sb.append(", gameContentTypeSupported ").append(gameContentTypeSupported);
sb.append(", density ").append(densityDpi);
sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi");
sb.append(", appVsyncOff ").append(appVsyncOffsetNanos);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c466640..ded7a92 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -569,6 +569,23 @@
}
}
+ private void setPreferMinimalPostProcessingLocked(int displayId, boolean isPreferred) {
+ LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null) {
+ DisplayDeviceInfo info =
+ display.getPrimaryDisplayDeviceLocked().getDisplayDeviceInfoLocked();
+
+ if (info.allmSupported) {
+ display.setAutoLowLatencyMode(isPreferred);
+ }
+ if (info.gameContentTypeSupported) {
+ display.setGameContentType(isPreferred);
+ }
+
+ scheduleTraversalLocked(false);
+ }
+ }
+
private DisplayInfo getDisplayInfoInternal(int displayId, int callingUid) {
synchronized (mSyncRoot) {
LogicalDisplay display = mLogicalDisplays.get(displayId);
@@ -1192,7 +1209,8 @@
}
private void setDisplayPropertiesInternal(int displayId, boolean hasContent,
- float requestedRefreshRate, int requestedModeId, boolean inTraversal) {
+ float requestedRefreshRate, int requestedModeId, boolean preferMinimalPostProcessing,
+ boolean inTraversal) {
synchronized (mSyncRoot) {
LogicalDisplay display = mLogicalDisplays.get(displayId);
if (display == null) {
@@ -1215,6 +1233,8 @@
}
mDisplayModeDirector.getAppRequestObserver().setAppRequestedMode(
displayId, requestedModeId);
+
+ setPreferMinimalPostProcessingLocked(displayId, preferMinimalPostProcessing);
}
}
@@ -2343,6 +2363,7 @@
}
private final class LocalService extends DisplayManagerInternal {
+
@Override
public void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager) {
@@ -2431,9 +2452,10 @@
@Override
public void setDisplayProperties(int displayId, boolean hasContent,
- float requestedRefreshRate, int requestedMode, boolean inTraversal) {
+ float requestedRefreshRate, int requestedMode, boolean preferMinimalPostProcessing,
+ boolean inTraversal) {
setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate,
- requestedMode, inTraversal);
+ requestedMode, preferMinimalPostProcessing, inTraversal);
}
@Override
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index b03dc3b..07f8253 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -179,6 +179,10 @@
private int mActiveColorMode;
private boolean mActiveColorModeInvalid;
private Display.HdrCapabilities mHdrCapabilities;
+ private boolean mAllmSupported;
+ private boolean mGameContentTypeSupported;
+ private boolean mAllmRequested;
+ private boolean mGameContentTypeRequested;
private boolean mSidekickActive;
private SidekickInternal mSidekickInternal;
@@ -202,6 +206,8 @@
mBacklight = null;
}
mHdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken);
+ mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
+ mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken);
}
@Override
@@ -395,6 +401,8 @@
mInfo.defaultModeId = mDefaultModeId;
mInfo.supportedModes = getDisplayModes(mSupportedModes);
mInfo.colorMode = mActiveColorMode;
+ mInfo.allmSupported = mAllmSupported;
+ mInfo.gameContentTypeSupported = mGameContentTypeSupported;
mInfo.supportedColorModes =
new int[mSupportedColorModes.size()];
for (int i = 0; i < mSupportedColorModes.size(); i++) {
@@ -730,6 +738,40 @@
}
@Override
+ public void setAutoLowLatencyModeLocked(boolean on) {
+ if (mAllmRequested == on) {
+ return;
+ }
+
+ mAllmRequested = on;
+
+ if (!mAllmSupported) {
+ Slog.d(TAG, "Unable to set ALLM because the connected display "
+ + "does not support ALLM.");
+ return;
+ }
+
+ SurfaceControl.setAutoLowLatencyMode(getDisplayTokenLocked(), on);
+ }
+
+ @Override
+ public void setGameContentTypeLocked(boolean on) {
+ if (mGameContentTypeRequested == on) {
+ return;
+ }
+
+ mGameContentTypeRequested = on;
+
+ if (!mGameContentTypeSupported) {
+ Slog.d(TAG, "Unable to set game content type because the connected "
+ + "display does not support game content type.");
+ return;
+ }
+
+ SurfaceControl.setGameContentType(getDisplayTokenLocked(), on);
+ }
+
+ @Override
public void dumpLocked(PrintWriter pw) {
super.dumpLocked(pw);
pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId);
@@ -743,6 +785,10 @@
pw.println("mState=" + Display.stateToString(mState));
pw.println("mBrightness=" + mBrightness);
pw.println("mBacklight=" + mBacklight);
+ pw.println("mAllmSupported=" + mAllmSupported);
+ pw.println("mAllmRequested=" + mAllmRequested);
+ pw.println("mGameContentTypeSupported" + mGameContentTypeSupported);
+ pw.println("mGameContentTypeRequested" + mGameContentTypeRequested);
pw.println("mDisplayInfos=");
for (int i = 0; i < mDisplayInfos.length; i++) {
pw.println(" " + mDisplayInfos[i]);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index dcef998..ca431bc 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -89,6 +89,8 @@
private int[] mAllowedDisplayModes = new int[0];
private int mRequestedColorMode;
+ private boolean mShouldSetAllm;
+ private boolean mShouldSetGameContentType;
// The display offsets to apply to the display projection.
private int mDisplayOffsetX;
@@ -282,6 +284,8 @@
deviceInfo.supportedColorModes,
deviceInfo.supportedColorModes.length);
mBaseDisplayInfo.hdrCapabilities = deviceInfo.hdrCapabilities;
+ mBaseDisplayInfo.minimalPostProcessingSupported =
+ deviceInfo.allmSupported || deviceInfo.gameContentTypeSupported;
mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;
mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi;
@@ -360,6 +364,9 @@
device.setRequestedColorModeLocked(0);
}
+ device.setAutoLowLatencyModeLocked(mShouldSetAllm);
+ device.setGameContentTypeLocked(mShouldSetGameContentType);
+
// Only grab the display info now as it may have been changed based on the requests above.
final DisplayInfo displayInfo = getDisplayInfoLocked();
final DisplayDeviceInfo displayDeviceInfo = device.getDisplayDeviceInfoLocked();
@@ -481,6 +488,26 @@
mRequestedColorMode = colorMode;
}
+ /**
+ * Sends the Auto Low Latency Mode (ALLM) signal over HDMI, or requests an internal display to
+ * switch to a low-latency mode.
+ *
+ * @param on Whether to set ALLM on or off.
+ */
+ public void setAutoLowLatencyMode(boolean on) {
+ mShouldSetAllm = on;
+ }
+
+ /**
+ * Sends a ContentType=Game signal over HDMI, or requests an internal display to switch to a
+ * game mode (generally lower latency).
+ *
+ * @param on Whether to send a ContentType=Game signal or not
+ */
+ public void setGameContentType(boolean on) {
+ mShouldSetGameContentType = on;
+ }
+
/** Returns the pending requested color mode. */
public int getRequestedColorModeLocked() {
return mRequestedColorMode;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 456f650..28195fa 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -737,6 +737,7 @@
// Update effect.
w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
+
if (!mTmpApplySurfaceChangesTransactionState.obscured) {
final boolean isDisplayed = w.isDisplayedLw();
@@ -767,6 +768,10 @@
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate
= w.mAttrs.preferredRefreshRate;
}
+
+ mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing
+ |= w.mAttrs.preferMinimalPostProcessing;
+
final int preferredModeId = getDisplayPolicy().getRefreshRatePolicy()
.getPreferredModeId(w);
if (mTmpApplySurfaceChangesTransactionState.preferredModeId == 0
@@ -3605,6 +3610,7 @@
mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferredModeId,
+ mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
true /* inTraversal, must call performTraversalInTrans... below */);
final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
@@ -3864,6 +3870,7 @@
boolean displayHasContent;
boolean obscured;
boolean syswin;
+ boolean preferMinimalPostProcessing;
float preferredRefreshRate;
int preferredModeId;
@@ -3871,6 +3878,7 @@
displayHasContent = false;
obscured = false;
syswin = false;
+ preferMinimalPostProcessing = false;
preferredRefreshRate = 0;
preferredModeId = 0;
}