Allow DeviceConfig to change display settings

Allow DeviceConfig to change default refresh rate and 60 hz only
thresholds.

Bug: 139138964
Change-Id: I3bf5f8cf7a99e4723265138004fbbd08102119d9
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 7648636..4681cfe 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -38,13 +38,16 @@
 import android.os.UserHandle;
 import android.os.PowerManager;
 import android.os.SystemClock;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayInfo;
 
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.R;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
 import com.android.server.display.whitebalance.AmbientFilter;
@@ -64,6 +67,8 @@
     private static final boolean DEBUG = false;
 
     private static final int MSG_ALLOWED_MODES_CHANGED = 1;
+    private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
+    private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
 
     // Special ID used to indicate that given vote is to be applied globally, rather than to a
     // specific display.
@@ -91,6 +96,7 @@
     private final DisplayObserver mDisplayObserver;
     private final BrightnessObserver mBrightnessObserver;
 
+    private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
     private Listener mListener;
 
     public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
@@ -103,7 +109,7 @@
         mSettingsObserver = new SettingsObserver(context, handler);
         mDisplayObserver = new DisplayObserver(context, handler);
         mBrightnessObserver = new BrightnessObserver(context, handler);
-
+        mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
     }
 
     /**
@@ -405,7 +411,7 @@
         void onAllowedDisplayModesChanged();
     }
 
-    private static final class DisplayModeDirectorHandler extends Handler {
+    private final class DisplayModeDirectorHandler extends Handler {
         DisplayModeDirectorHandler(Looper looper) {
             super(looper, null, true /*async*/);
         }
@@ -417,6 +423,23 @@
                     Listener listener = (Listener) msg.obj;
                     listener.onAllowedDisplayModesChanged();
                     break;
+
+                case MSG_BRIGHTNESS_THRESHOLDS_CHANGED:
+                    Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
+
+                    if (thresholds != null) {
+                        mBrightnessObserver.onDeviceConfigThresholdsChanged(
+                                thresholds.first, thresholds.second);
+                    } else {
+                        mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null);
+                    }
+                    break;
+
+                case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
+                    Float defaultPeakRefreshRate = (Float) msg.obj;
+                    mSettingsObserver.onDeviceConfigDefaultPeakRefreshRateChanged(
+                            defaultPeakRefreshRate);
+                    break;
             }
         }
     }
@@ -508,7 +531,7 @@
                 Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
 
         private final Context mContext;
-        private final float mDefaultPeakRefreshRate;
+        private float mDefaultPeakRefreshRate;
 
         SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
             super(handler);
@@ -523,12 +546,33 @@
                     UserHandle.USER_SYSTEM);
             cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
                     UserHandle.USER_SYSTEM);
+
+            Float deviceConfigDefaultPeakRefresh =
+                    mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
+            if (deviceConfigDefaultPeakRefresh != null) {
+                mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh;
+            }
+
             synchronized (mLock) {
                 updateRefreshRateSettingLocked();
                 updateLowPowerModeSettingLocked();
             }
         }
 
+        public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
+            if (defaultPeakRefreshRate == null) {
+                defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
+                        R.integer.config_defaultPeakRefreshRate);
+            }
+
+            if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) {
+                synchronized (mLock) {
+                    mDefaultPeakRefreshRate = defaultPeakRefreshRate;
+                    updateRefreshRateSettingLocked();
+                }
+            }
+        }
+
         @Override
         public void onChange(boolean selfChange, Uri uri, int userId) {
             synchronized (mLock) {
@@ -721,8 +765,8 @@
                 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
 
         private final static int LIGHT_SENSOR_RATE_MS = 250;
-        private final int[] mDisplayBrightnessThresholds;
-        private final int[] mAmbientBrightnessThresholds;
+        private int[] mDisplayBrightnessThresholds;
+        private int[] mAmbientBrightnessThresholds;
         // valid threshold if any item from the array >= 0
         private boolean mShouldObserveDisplayChange;
         private boolean mShouldObserveAmbientChange;
@@ -735,8 +779,10 @@
         private AmbientFilter mAmbientFilter;
 
         private final Context mContext;
-        // Enable light sensor only when screen is on, peak refresh rate enabled and low power mode
-        // off. After initialization, these states will be updated from the same handler thread.
+
+        // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak
+        // refresh rate enabled and low power mode off. After initialization, these states will
+        // be updated from the same handler thread.
         private boolean mScreenOn = false;
         private boolean mPeakRefreshRateEnabled = false;
         private boolean mLowPowerModeEnabled = false;
@@ -748,79 +794,56 @@
                     R.array.config_brightnessThresholdsOfPeakRefreshRate);
             mAmbientBrightnessThresholds = context.getResources().getIntArray(
                     R.array.config_ambientThresholdsOfPeakRefreshRate);
+
             if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) {
                 throw new RuntimeException("display brightness threshold array and ambient "
                         + "brightness threshold array have different length");
             }
-
-            mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
-            mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
         }
 
         public void observe(SensorManager sensorManager) {
-            if (mShouldObserveDisplayChange) {
-                final ContentResolver cr = mContext.getContentResolver();
-                cr.registerContentObserver(mDisplayBrightnessSetting,
-                        false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
+            mSensorManager = sensorManager;
+            // DeviceConfig is accessible after system ready.
+            int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds();
+            int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds();
+
+            if (brightnessThresholds != null && ambientThresholds != null
+                    && brightnessThresholds.length == ambientThresholds.length) {
+                mDisplayBrightnessThresholds = brightnessThresholds;
+                mAmbientBrightnessThresholds = ambientThresholds;
             }
-
-            if (mShouldObserveAmbientChange) {
-                Resources resources = mContext.getResources();
-                String lightSensorType = resources.getString(
-                        com.android.internal.R.string.config_displayLightSensorType);
-
-                Sensor lightSensor = null;
-                if (!TextUtils.isEmpty(lightSensorType)) {
-                    List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
-                    for (int i = 0; i < sensors.size(); i++) {
-                        Sensor sensor = sensors.get(i);
-                        if (lightSensorType.equals(sensor.getStringType())) {
-                            lightSensor = sensor;
-                            break;
-                        }
-                    }
-                }
-
-                if (lightSensor == null) {
-                    lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
-                }
-
-                if (lightSensor != null) {
-                    final Resources res = mContext.getResources();
-
-                    mAmbientFilter = DisplayWhiteBalanceFactory.createBrightnessFilter(res);
-                    mSensorManager = sensorManager;
-                    mLightSensor = lightSensor;
-
-                    onScreenOn(isDefaultDisplayOn());
-                }
-            }
-
-            if (mShouldObserveDisplayChange || mShouldObserveAmbientChange) {
-                synchronized (mLock) {
-                    onBrightnessChangedLocked();
-                }
-            }
+            restartObserver();
+            mDeviceConfigDisplaySettings.startListening();
         }
 
         public void onPeakRefreshRateEnabled(boolean b) {
-            if (mShouldObserveAmbientChange && mPeakRefreshRateEnabled != b) {
+            if (mPeakRefreshRateEnabled != b) {
                 mPeakRefreshRateEnabled = b;
                 updateSensorStatus();
             }
         }
 
         public void onLowPowerModeEnabled(boolean b) {
-            if (mShouldObserveAmbientChange && mLowPowerModeEnabled != b) {
+            if (mLowPowerModeEnabled != b) {
                 mLowPowerModeEnabled = b;
                 updateSensorStatus();
             }
         }
 
-        public void onDisplayChanged(int displayId) {
-            if (displayId == Display.DEFAULT_DISPLAY) {
-                onScreenOn(isDefaultDisplayOn());
+        public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds,
+                int[] ambientThresholds) {
+            if (brightnessThresholds != null && ambientThresholds != null
+                    && brightnessThresholds.length == ambientThresholds.length) {
+                mDisplayBrightnessThresholds = brightnessThresholds;
+                mAmbientBrightnessThresholds = ambientThresholds;
+            } else {
+                // Invalid or empty. Use device default.
+                mDisplayBrightnessThresholds = mContext.getResources().getIntArray(
+                        R.array.config_brightnessThresholdsOfPeakRefreshRate);
+                mAmbientBrightnessThresholds = mContext.getResources().getIntArray(
+                        R.array.config_ambientThresholdsOfPeakRefreshRate);
             }
+            restartObserver();
         }
 
         public void dumpLocked(PrintWriter pw) {
@@ -835,6 +858,12 @@
             }
         }
 
+        public void onDisplayChanged(int displayId) {
+            if (displayId == Display.DEFAULT_DISPLAY) {
+                onScreenOn(isDefaultDisplayOn());
+            }
+        }
+
         @Override
         public void onChange(boolean selfChange, Uri uri, int userId) {
             synchronized (mLock) {
@@ -842,6 +871,61 @@
             }
         }
 
+        private void restartObserver() {
+            mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
+            mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
+
+            final ContentResolver cr = mContext.getContentResolver();
+            if (mShouldObserveDisplayChange) {
+                // Content Service does not check if an listener has already been registered.
+                // To ensure only one listener is registered, force an unregistration first.
+                cr.unregisterContentObserver(this);
+                cr.registerContentObserver(mDisplayBrightnessSetting,
+                        false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
+            } else {
+                cr.unregisterContentObserver(this);
+            }
+
+            if (mShouldObserveAmbientChange) {
+                Resources resources = mContext.getResources();
+                String lightSensorType = resources.getString(
+                        com.android.internal.R.string.config_displayLightSensorType);
+
+                Sensor lightSensor = null;
+                if (!TextUtils.isEmpty(lightSensorType)) {
+                    List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+                    for (int i = 0; i < sensors.size(); i++) {
+                        Sensor sensor = sensors.get(i);
+                        if (lightSensorType.equals(sensor.getStringType())) {
+                            lightSensor = sensor;
+                            break;
+                        }
+                    }
+                }
+
+                if (lightSensor == null) {
+                    lightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+                }
+
+                if (lightSensor != null) {
+                    final Resources res = mContext.getResources();
+
+                    mAmbientFilter = DisplayWhiteBalanceFactory.createBrightnessFilter(res);
+                    mLightSensor = lightSensor;
+
+                    onScreenOn(isDefaultDisplayOn());
+                }
+            } else {
+                mAmbientFilter = null;
+                mLightSensor = null;
+            }
+
+            updateSensorStatus();
+            synchronized (mLock) {
+                onBrightnessChangedLocked();
+            }
+        }
+
         /**
          * Checks to see if at least one value is positive, in which case it is necessary to listen
          * to value changes.
@@ -903,7 +987,8 @@
                 return;
             }
 
-            if (mScreenOn && !mLowPowerModeEnabled && mPeakRefreshRateEnabled) {
+            if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled
+                    && mPeakRefreshRateEnabled) {
                 mSensorManager.registerListener(mLightSensorListener,
                         mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
             } else {
@@ -996,6 +1081,88 @@
                     }
                 }
             };
-        };
+        }
     }
+
+    private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
+
+        public DeviceConfigDisplaySettings() {
+        }
+
+        public void startListening() {
+            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    BackgroundThread.getExecutor(), this);
+        }
+
+        /*
+         * Return null if no such property or wrong format (not comma separated integers).
+         */
+        public int[] getBrightnessThresholds() {
+            return getIntArrayProperty(
+                    DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_BRIGHTNESS_THRESHOLDS);
+        }
+
+        /*
+         * Return null if no such property or wrong format (not comma separated integers).
+         */
+        public int[] getAmbientThresholds() {
+            return getIntArrayProperty(
+                    DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_AMBIENT_THRESHOLDS);
+        }
+
+        /*
+         * Return null if no such property
+         */
+        public Float getDefaultPeakRefreshRate() {
+            float defaultPeakRefreshRate = DeviceConfig.getFloat(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
+
+            if (defaultPeakRefreshRate == -1) {
+                return null;
+            }
+            return defaultPeakRefreshRate;
+        }
+
+        @Override
+        public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
+            int[] brightnessThresholds = getBrightnessThresholds();
+            int[] ambientThresholds = getAmbientThresholds();
+            Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
+
+            mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED,
+                    new Pair<int[], int[]>(brightnessThresholds, ambientThresholds))
+                    .sendToTarget();
+            mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
+                    defaultPeakRefreshRate).sendToTarget();
+        }
+
+        private int[] getIntArrayProperty(String prop) {
+            String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
+                    null);
+
+            if (strArray != null) {
+                return parseIntArray(strArray);
+            }
+
+            return null;
+        }
+
+        private int[] parseIntArray(@NonNull String strArray) {
+            String[] items = strArray.split(",");
+            int[] array = new int[items.length];
+
+            try {
+                for (int i = 0; i < array.length; i++) {
+                    array[i] = Integer.parseInt(items[i]);
+                }
+            } catch (NumberFormatException e) {
+                Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
+                array = null;
+            }
+
+            return array;
+        }
+    }
+
 }