Add TestApi for setRefreshRateSwitchingType

This CLs adds a test API to replicted the behaviour of the
MATCH_CONTENT_FRAME_RATE user setting. This is intended to
be used from CTS.

Bug: 157504046
Bug: 171952409
Test: atest MatchContentFrameRateTest
Change-Id: I90d9a0d019cdb908b3b13328a78fe370676ad064
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e392ed7..51edd03 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -18,6 +18,7 @@
     field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES";
     field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
+    field public static final String MODIFY_REFRESH_RATE_SWITCHING_TYPE = "android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE";
     field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
     field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
     field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS";
@@ -743,9 +744,14 @@
   }
 
   public final class DisplayManager {
+    method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public int getRefreshRateSwitchingType();
     method public boolean isMinimalPostProcessingRequested(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int);
     method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
     method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode();
+    field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2
+    field public static final int SWITCHING_TYPE_NONE = 0; // 0x0
+    field public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; // 0x1
     field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200
     field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
   }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index ca5eeb1..9bae1ff 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -19,6 +19,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.Manifest;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -38,9 +39,12 @@
 import android.view.Display;
 import android.view.Surface;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
+
 /**
  * Manages the properties of attached displays.
  */
@@ -336,6 +340,40 @@
      */
     public static final int VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP = 1 << 11;
 
+
+    /** @hide */
+    @IntDef(prefix = {"SWITCHING_TYPE_"}, value = {
+            SWITCHING_TYPE_NONE,
+            SWITCHING_TYPE_WITHIN_GROUPS,
+            SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SwitchingType {}
+
+    /**
+     * No mode switching will happen.
+     * @hide
+     */
+    @TestApi
+    public static final int SWITCHING_TYPE_NONE = 0;
+
+    /**
+     * Allow only refresh rate switching between modes in the same configuration group. This way
+     * only switches without visual interruptions for the user will be allowed.
+     * @hide
+     */
+    @TestApi
+    public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1;
+
+    /**
+     * Allow refresh rate switching between all refresh rates even if the switch with have visual
+     * interruptions for the user.
+     * @hide
+     */
+    @TestApi
+    public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2;
+
+
     /** @hide */
     public DisplayManager(Context context) {
         mContext = context;
@@ -875,6 +913,29 @@
     }
 
     /**
+     * Sets the refresh rate switching type.
+     * This matches {@link android.provider.Settings.Secure.MATCH_CONTENT_FRAME_RATE}
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE)
+    public void setRefreshRateSwitchingType(@SwitchingType int newValue) {
+        mGlobal.setRefreshRateSwitchingType(newValue);
+    }
+
+    /**
+     * Returns the refresh rate switching type.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE)
+    @SwitchingType public int getRefreshRateSwitchingType() {
+        return mGlobal.getRefreshRateSwitchingType();
+    }
+
+    /**
      * Listens for changes in available display devices.
      */
     public interface DisplayListener {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 7b4889f..77ae947 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -725,6 +725,33 @@
         }
     }
 
+    /**
+     * Sets the refresh rate switching type.
+     *
+     * @hide
+     */
+    public void setRefreshRateSwitchingType(@DisplayManager.SwitchingType int newValue) {
+        try {
+            mDm.setRefreshRateSwitchingType(newValue);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the refresh rate switching type.
+     *
+     * @hide
+     */
+    @DisplayManager.SwitchingType
+    public int getRefreshRateSwitchingType() {
+        try {
+            return mDm.getRefreshRateSwitchingType();
+        } 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 85da642..a9f78fa 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -134,4 +134,10 @@
     // battery etc.
     void setShouldAlwaysRespectAppRequestedMode(boolean enabled);
     boolean shouldAlwaysRespectAppRequestedMode();
+
+    // Sets the refresh rate switching type.
+    void setRefreshRateSwitchingType(int newValue);
+
+    // Returns the refresh rate switching type.
+    int getRefreshRateSwitchingType();
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 23e55cd..98f7255 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4031,6 +4031,13 @@
     <permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS"
                 android:protectionLevel="signature" />
 
+    <!-- Allows an application to modify the refresh rate switching type. This
+         matches Setting.Secure.MATCH_CONTENT_FRAME_RATE.
+         @hide
+         @TestApi -->
+    <permission android:name="android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE"
+                android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to control VPN.
          <p>Not for use by third-party applications.</p>
          @hide -->
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 2e3ea24..92b1ca7 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -332,6 +332,9 @@
     <!-- Permission needed for CTS test - DisplayTest -->
     <uses-permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS" />
 
+    <!-- Permission needed for CTS test - MatchContentFrameRateTest -->
+    <uses-permission android:name="android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE" />
+
     <!-- Permission needed for CTS test - TimeManagerTest -->
     <uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 29b413d..d4a19d6 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -53,6 +53,7 @@
 import android.hardware.display.BrightnessChangeEvent;
 import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.Curve;
+import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerGlobal;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
@@ -1252,11 +1253,19 @@
         mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled);
     }
 
-
     boolean shouldAlwaysRespectAppRequestedModeInternal() {
         return mDisplayModeDirector.shouldAlwaysRespectAppRequestedMode();
     }
 
+    void setRefreshRateSwitchingTypeInternal(@DisplayManager.SwitchingType int newValue) {
+        mDisplayModeDirector.setModeSwitchingType(newValue);
+    }
+
+    @DisplayManager.SwitchingType
+    int getRefreshRateSwitchingTypeInternal() {
+        return mDisplayModeDirector.getModeSwitchingType();
+    }
+
     private void setBrightnessConfigurationForUserInternal(
             @Nullable BrightnessConfiguration c, @UserIdInt int userId,
             @Nullable String packageName) {
@@ -2595,6 +2604,32 @@
             }
         }
 
+        @Override // Binder call
+        public void setRefreshRateSwitchingType(int newValue) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE,
+                    "Permission required to modify refresh rate switching type.");
+            final long token = Binder.clearCallingIdentity();
+            try {
+                setRefreshRateSwitchingTypeInternal(newValue);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
+        public int getRefreshRateSwitchingType() {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE,
+                    "Permission required read refresh rate switching type.");
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return getRefreshRateSwitchingTypeInternal();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         private boolean validatePackageName(int uid, String packageName) {
             if (packageName != null) {
                 String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 329081a..9591894 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -16,7 +16,6 @@
 
 package com.android.server.display;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ContentResolver;
@@ -53,8 +52,6 @@
 import com.android.server.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -110,28 +107,11 @@
 
     private boolean mAlwaysRespectAppRequest;
 
-    @IntDef(prefix = {"SWITCHING_TYPE_"}, value = {
-            SWITCHING_TYPE_NONE,
-            SWITCHING_TYPE_WITHIN_GROUPS,
-            SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SwitchingType {}
-
-    // No mode switching will happen.
-    public static final int SWITCHING_TYPE_NONE = 0;
-    // Allow only refresh rate switching between modes in the same configuration group. This way
-    // only switches without visual interruptions for the user will be allowed.
-    public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1;
-    // Allow refresh rate switching between all refresh rates even if the switch with have visual
-    // interruptions for the user.
-    public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2;
-
     /**
      * The allowed refresh rate switching type. This is used by SurfaceFlinger.
      */
-    @SwitchingType
-    private int mModeSwitchingType = SWITCHING_TYPE_WITHIN_GROUPS;
+    @DisplayManager.SwitchingType
+    private int mModeSwitchingType = DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
 
     public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
         this(context, handler, new RealInjector());
@@ -337,7 +317,7 @@
             if (availableModes.length > 0) {
                 baseModeId = availableModes[0];
             }
-            if (mModeSwitchingType == SWITCHING_TYPE_NONE) {
+            if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
                 Display.Mode baseMode = null;
                 for (Display.Mode mode : modes) {
                     if (mode.getModeId() == baseModeId) {
@@ -359,7 +339,7 @@
             }
 
             boolean allowGroupSwitching =
-                    mModeSwitchingType == SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
+                    mModeSwitchingType == DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
             return new DesiredDisplayModeSpecs(baseModeId,
                     allowGroupSwitching,
                     new RefreshRateRange(
@@ -450,18 +430,21 @@
 
     /**
      * Sets the display mode switching type.
-     * @param type
+     * @param newType
      */
-    public void setModeSwitchingType(@SwitchingType int type) {
+    public void setModeSwitchingType(@DisplayManager.SwitchingType int newType) {
         synchronized (mLock) {
-            mModeSwitchingType = type;
+            if (newType != mModeSwitchingType) {
+                mModeSwitchingType = newType;
+                notifyDesiredDisplayModeSpecsChangedLocked();
+            }
         }
     }
 
     /**
      * Returns the display mode switching type.
      */
-    @SwitchingType
+    @DisplayManager.SwitchingType
     public int getModeSwitchingType() {
         synchronized (mLock) {
             return mModeSwitchingType;
@@ -583,13 +566,13 @@
         }
     }
 
-    private static String switchingTypeToString(@SwitchingType int type) {
+    private static String switchingTypeToString(@DisplayManager.SwitchingType int type) {
         switch (type) {
-            case SWITCHING_TYPE_NONE:
+            case DisplayManager.SWITCHING_TYPE_NONE:
                 return "SWITCHING_TYPE_NONE";
-            case SWITCHING_TYPE_WITHIN_GROUPS:
+            case DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS:
                 return "SWITCHING_TYPE_WITHIN_GROUPS";
-            case SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS:
+            case DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS:
                 return "SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS";
             default:
                 return "Unknown SwitchingType " + type;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index cb5ca04..603608b 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -40,6 +40,7 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
 import android.hardware.Sensor;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
@@ -402,7 +403,7 @@
 
         director.injectVotesByDisplay(votesByDisplay);
         assertThat(director.getModeSwitchingType())
-                .isNotEqualTo(DisplayModeDirector.SWITCHING_TYPE_NONE);
+                .isNotEqualTo(DisplayManager.SWITCHING_TYPE_NONE);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
 
         assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30);
@@ -411,9 +412,9 @@
         assertThat(desiredSpecs.appRequestRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.baseModeId).isEqualTo(30);
 
-        director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_NONE);
+        director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_NONE);
         assertThat(director.getModeSwitchingType())
-                .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_NONE);
+                .isEqualTo(DisplayManager.SWITCHING_TYPE_NONE);
 
         desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30);
@@ -428,9 +429,9 @@
         final int displayId = 0;
         DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
 
-        director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_WITHIN_GROUPS);
+        director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS);
         assertThat(director.getModeSwitchingType())
-                .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_WITHIN_GROUPS);
+                .isEqualTo(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.allowGroupSwitching).isFalse();
     }
@@ -440,9 +441,9 @@
         final int displayId = 0;
         DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
 
-        director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
+        director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
         assertThat(director.getModeSwitchingType())
-                .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
+                .isEqualTo(DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.allowGroupSwitching).isTrue();
     }