Merge "Add minimal post processing API to framework"
diff --git a/api/current.txt b/api/current.txt
index 11ae3d5..8f0002b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1061,6 +1061,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
@@ -11185,6 +11186,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;
@@ -49518,6 +49520,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
@@ -52543,6 +52546,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);
@@ -52860,6 +52864,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 577272e..9711e06 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7855,6 +7855,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 26193f6..fe59f05 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 ba25093..117b971 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 04e82c7..b84a7b5 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 87628da..db83ede 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -170,6 +170,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,
@@ -187,6 +189,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);
 
@@ -1670,6 +1675,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) {
@@ -1721,6 +1748,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 d79abc2..a168c07 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1197,6 +1197,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 9d5f98e..cbfe52b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2573,6 +2573,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
@@ -2754,6 +2781,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
@@ -2811,6 +2839,7 @@
             hideTimeoutMilliseconds = in.readLong();
             insetsFlags.appearance = in.readInt();
             insetsFlags.behavior = in.readInt();
+            preferMinimalPostProcessing = in.readBoolean();
         }
 
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -2856,6 +2885,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;
@@ -3036,6 +3067,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;
 
@@ -3175,6 +3211,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 c6e678ab..ec8d4ab 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1199,6 +1199,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) {
@@ -1402,6 +1430,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 166cde0..1025f81 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2494,6 +2494,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 78c4efe..d753630 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3051,6 +3051,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 7ce63c5..af68403 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -157,6 +157,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 d20191d..3e8a726 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 308c755..86ba845 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -181,6 +181,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;
 
@@ -204,6 +208,8 @@
                 mBacklight = null;
             }
             mHdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken);
+            mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
+            mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken);
         }
 
         @Override
@@ -397,6 +403,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++) {
@@ -767,6 +775,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);
@@ -782,6 +824,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 f4b2dc8..4649374 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;
@@ -361,6 +365,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();
@@ -482,6 +489,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 9e5734b..fb84832 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -744,6 +744,7 @@
 
         // Update effect.
         w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
+
         if (!mTmpApplySurfaceChangesTransactionState.obscured) {
             final boolean isDisplayed = w.isDisplayedLw();
 
@@ -774,6 +775,10 @@
                     mTmpApplySurfaceChangesTransactionState.preferredRefreshRate
                             = w.mAttrs.preferredRefreshRate;
                 }
+
+                mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing
+                        |= w.mAttrs.preferMinimalPostProcessing;
+
                 final int preferredModeId = getDisplayPolicy().getRefreshRatePolicy()
                         .getPreferredModeId(w);
                 if (mTmpApplySurfaceChangesTransactionState.preferredModeId == 0
@@ -3620,6 +3625,7 @@
                 mLastHasContent,
                 mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
                 mTmpApplySurfaceChangesTransactionState.preferredModeId,
+                mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
                 true /* inTraversal, must call performTraversalInTrans... below */);
 
         final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
@@ -3923,6 +3929,7 @@
         boolean displayHasContent;
         boolean obscured;
         boolean syswin;
+        boolean preferMinimalPostProcessing;
         float preferredRefreshRate;
         int preferredModeId;
 
@@ -3930,6 +3937,7 @@
             displayHasContent = false;
             obscured = false;
             syswin = false;
+            preferMinimalPostProcessing = false;
             preferredRefreshRate = 0;
             preferredModeId = 0;
         }