Merge cherrypicks of [9428684, 9428685, 9428686, 9428687, 9428607, 9428608, 9428514, 9428272, 9428273, 9428275, 9428276, 9428277, 9428278, 9428689, 9428690, 9428691, 9428478, 9428702, 9428692, 9428479, 9428720, 9428721, 9428722, 9428723, 9428703, 9426151, 9428693, 9428724, 9428725, 9428726, 9428727, 9428728, 9428729, 9428730, 9428731, 9428732, 9428733, 9428734, 9428704, 9428609, 9428610, 9428611, 9428612, 9428613, 9428614, 9428615, 9426152, 9426153, 9426154, 9426155, 9426156, 9426157, 9426158, 9426159, 9428740, 9428409, 9428694, 9428695, 9428735] into sparse-5888870-L93800000368094851

Change-Id: I39c7cdefab1fbe10602d171c5c64c334bfb0a91a
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 5d8fa92..0b25dbd 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -829,23 +829,36 @@
     public interface DeviceConfig {
 
         /**
-         * Key for accessing the 60 hz only regions.
+         * Key for refresh rate in the zone defined by thresholds.
+         *
+         * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+         * @see android.R.integer#config_defaultZoneBehavior
+         */
+        String KEY_REFRESH_RATE_IN_ZONE = "refresh_rate_in_zone";
+
+        /**
+         * Key for accessing the display brightness thresholds for the configured refresh rate zone.
+         * The value will be a pair of comma separated integers representing the minimum and maximum
+         * thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]).
          *
          * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
          * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
          * @hide
          */
-        String KEY_PEAK_REFRESH_RATE_BRIGHTNESS_THRESHOLDS =
+        String KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS =
                 "peak_refresh_rate_brightness_thresholds";
 
         /**
-         * Key for accessing the 60 hz only regions.
+         * Key for accessing the ambient brightness thresholds for the configured refresh rate zone.
+         * The value will be a pair of comma separated integers representing the minimum and maximum
+         * thresholds of the zone, respectively, in lux.
          *
          * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
-         * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
+         * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
          * @hide
          */
-        String KEY_PEAK_REFRESH_RATE_AMBIENT_THRESHOLDS = "peak_refresh_rate_ambient_thresholds";
+        String KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS =
+                "peak_refresh_rate_ambient_thresholds";
 
         /**
          * Key for default peak refresh rate
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index df87981..1577a22 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4167,6 +4167,10 @@
         -->
     </integer-array>
 
+    <!-- Default refresh rate in the zone defined by brightness and ambient thresholds.
+         If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
+    <integer name="config_defaultRefreshRateInZone">0</integer>
+
     <!-- The type of the light sensor to be used by the display framework for things like
          auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. -->
     <string name="config_displayLightSensorType" translatable="false" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 92c5d2c..0d9c4b3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3791,6 +3791,7 @@
 
   <!-- For high refresh rate displays -->
   <java-symbol type="integer" name="config_defaultPeakRefreshRate" />
+  <java-symbol type="integer" name="config_defaultRefreshRateInZone" />
   <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
   <java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" />
 
diff --git a/data/keyboards/Vendor_045e_Product_02dd.kl b/data/keyboards/Vendor_045e_Product_02dd.kl
new file mode 100644
index 0000000..3975cec
--- /dev/null
+++ b/data/keyboards/Vendor_045e_Product_02dd.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# XBox One Controller - Model 1697 - USB
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304   BUTTON_A
+key 305   BUTTON_B
+key 307   BUTTON_X
+key 308   BUTTON_Y
+
+key 310   BUTTON_L1
+key 311   BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317   BUTTON_THUMBL
+key 318   BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314   BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315   BUTTON_START
+
+# Xbox key
+key 316   BUTTON_MODE
diff --git a/data/keyboards/Vendor_045e_Product_02fd.kl b/data/keyboards/Vendor_045e_Product_02fd.kl
index 512f7e1..1b03497 100644
--- a/data/keyboards/Vendor_045e_Product_02fd.kl
+++ b/data/keyboards/Vendor_045e_Product_02fd.kl
@@ -53,5 +53,10 @@
 # Hamburger - 3 parallel lines
 key 315   BUTTON_START
 
-# Xbox key
+# There are at least two versions of firmware out for this controller.
+# They send different linux keys for the "Xbox" button.
+# Xbox key (original firmware)
 key 172   BUTTON_MODE
+
+# Xbox key (newer firmware)
+key 316   BUTTON_MODE
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 340cb3a..6e8e823 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -196,9 +196,6 @@
     <!-- Doze: duration to avoid false pickup gestures triggered by notification vibrations -->
     <integer name="doze_pickup_vibration_threshold">2000</integer>
 
-    <!-- Doze: can we assume the pickup sensor includes a proximity check? -->
-    <bool name="doze_pickup_performs_proximity_check">false</bool>
-
     <!-- Type of a sensor that provides a low-power estimate of the desired display
          brightness, suitable to listen to while the device is asleep (e.g. during
          always-on display) -->
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 092eb46..026a625 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -107,8 +107,7 @@
                         config.dozePickupSensorAvailable(),
                         DozeLog.REASON_SENSOR_PICKUP, false /* touchCoords */,
                         false /* touchscreen */,
-                        false /* ignoresSetting */,
-                        mDozeParameters.getPickupPerformsProxCheck()),
+                        false /* ignoresSetting */),
                 new TriggerSensor(
                         findSensorWithType(config.doubleTapSensorType()),
                         Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
@@ -205,11 +204,8 @@
     public void updateListening() {
         boolean anyListening = false;
         for (TriggerSensor s : mSensors) {
-            // We don't want to be listening while we're PAUSED (prox sensor is covered)
-            // except when the sensor is already gated by prox.
-            boolean listen = mListening && (!mPaused || s.performsProxCheck());
-            s.setListening(listen);
-            if (listen) {
+            s.setListening(mListening);
+            if (mListening) {
                 anyListening = true;
             }
         }
@@ -391,7 +387,6 @@
         private final boolean mReportsTouchCoordinates;
         private final boolean mSettingDefault;
         private final boolean mRequiresTouchscreen;
-        private final boolean mSensorPerformsProxCheck;
 
         protected boolean mRequested;
         protected boolean mRegistered;
@@ -408,14 +403,12 @@
                 boolean configured, int pulseReason, boolean reportsTouchCoordinates,
                 boolean requiresTouchscreen) {
             this(sensor, setting, settingDef, configured, pulseReason, reportsTouchCoordinates,
-                    requiresTouchscreen, false /* ignoresSetting */,
-                    false /* sensorPerformsProxCheck */);
+                    requiresTouchscreen, false /* ignoresSetting */);
         }
 
         private TriggerSensor(Sensor sensor, String setting, boolean settingDef,
                 boolean configured, int pulseReason, boolean reportsTouchCoordinates,
-                boolean requiresTouchscreen, boolean ignoresSetting,
-                boolean sensorPerformsProxCheck) {
+                boolean requiresTouchscreen, boolean ignoresSetting) {
             mSensor = sensor;
             mSetting = setting;
             mSettingDefault = settingDef;
@@ -424,7 +417,6 @@
             mReportsTouchCoordinates = reportsTouchCoordinates;
             mRequiresTouchscreen = requiresTouchscreen;
             mIgnoresSetting = ignoresSetting;
-            mSensorPerformsProxCheck = sensorPerformsProxCheck;
         }
 
         public void setListening(boolean listen) {
@@ -498,23 +490,13 @@
                     screenX = event.values[0];
                     screenY = event.values[1];
                 }
-                mCallback.onSensorPulse(mPulseReason, mSensorPerformsProxCheck, screenX, screenY,
-                        event.values);
+                mCallback.onSensorPulse(mPulseReason, screenX, screenY, event.values);
                 if (!mRegistered) {
                     updateListening();  // reregister, this sensor only fires once
                 }
             }));
         }
 
-        /**
-         * If the sensor itself performs proximity checks, to avoid pocket dialing.
-         * Gated sensors don't need to be stopped when the {@link DozeMachine} is
-         * {@link DozeMachine.State#DOZE_AOD_PAUSED}.
-         */
-        public boolean performsProxCheck() {
-            return mSensorPerformsProxCheck;
-        }
-
         public void registerSettingsObserver(ContentObserver settingsObserver) {
             if (mConfigured && !TextUtils.isEmpty(mSetting)) {
                 mResolver.registerContentObserver(
@@ -610,8 +592,7 @@
                     return;
                 }
                 if (DEBUG) Log.d(TAG, "onSensorEvent: " + triggerEventToString(event));
-                mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
-                        event.getValues());
+                mCallback.onSensorPulse(mPulseReason, -1, -1, event.getValues());
             }));
         }
     }
@@ -621,13 +602,11 @@
         /**
          * Called when a sensor requests a pulse
          * @param pulseReason Requesting sensor, e.g. {@link DozeLog#REASON_SENSOR_PICKUP}
-         * @param sensorPerformedProxCheck true if the sensor already checked for FAR proximity.
          * @param screenX the location on the screen where the sensor fired or -1
- *                if the sensor doesn't support reporting screen locations.
+         *                if the sensor doesn't support reporting screen locations.
          * @param screenY the location on the screen where the sensor fired or -1
          * @param rawValues raw values array from the event.
          */
-        void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck,
-                float screenX, float screenY, float[] rawValues);
+        void onSensorPulse(int pulseReason, float screenX, float screenY, float[] rawValues);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 310f04a..bab64db 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -41,6 +41,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.Preconditions;
 import com.android.systemui.Dependency;
+import com.android.systemui.R;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.Assert;
@@ -156,8 +157,7 @@
     }
 
     @VisibleForTesting
-    void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
-            float screenX, float screenY, float[] rawValues) {
+    void onSensor(int pulseReason, float screenX, float screenY, float[] rawValues) {
         boolean isDoubleTap = pulseReason == DozeLog.REASON_SENSOR_DOUBLE_TAP;
         boolean isTap = pulseReason == DozeLog.REASON_SENSOR_TAP;
         boolean isPickup = pulseReason == DozeLog.REASON_SENSOR_PICKUP;
@@ -169,10 +169,11 @@
         if (isWakeDisplay) {
             onWakeScreen(wakeEvent, mMachine.isExecutingTransition() ? null : mMachine.getState());
         } else if (isLongPress) {
-            requestPulse(pulseReason, sensorPerformedProxCheck, null /* onPulseSupressedListener */);
+            requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
+                    null /* onPulseSupressedListener */);
         } else if (isWakeLockScreen) {
             if (wakeEvent) {
-                requestPulse(pulseReason, sensorPerformedProxCheck,
+                requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
                         null /* onPulseSupressedListener */);
             }
         } else {
@@ -191,8 +192,7 @@
                 } else {
                     mDozeHost.extendPulse(pulseReason);
                 }
-            }, sensorPerformedProxCheck
-                    || (mDockManager != null && mDockManager.isDocked()), pulseReason);
+            }, true /* alreadyPerformedProxCheck */, pulseReason);
         }
 
         if (isPickup) {
@@ -278,7 +278,7 @@
                             .setType(MetricsEvent.TYPE_OPEN)
                             .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
                 }
-            }, false /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
+            }, true /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
         } else {
             boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
             boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
@@ -417,6 +417,9 @@
         mDozeSensors.dump(pw);
     }
 
+    /**
+     * @see DozeSensors.ProxSensor
+     */
     private abstract class ProximityCheck implements SensorEventListener, Runnable {
         private static final int TIMEOUT_DELAY_MS = 500;
 
@@ -428,12 +431,18 @@
         private boolean mRegistered;
         private boolean mFinished;
         private float mMaxRange;
+        private boolean mUsingBrightnessSensor;
 
         protected abstract void onProximityResult(int result);
 
         public void check() {
             Preconditions.checkState(!mFinished && !mRegistered);
-            final Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+            Sensor sensor = DozeSensors.findSensorWithType(mSensorManager,
+                    mContext.getString(R.string.doze_brightness_sensor_type));
+            mUsingBrightnessSensor = sensor != null;
+            if (sensor == null) {
+                sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+            }
             if (sensor == null) {
                 if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: No sensor found");
                 finishWithResult(RESULT_UNKNOWN);
@@ -449,6 +458,9 @@
             mRegistered = true;
         }
 
+        /**
+         * @see DozeSensors.ProxSensor#onSensorChanged(SensorEvent)
+         */
         @Override
         public void onSensorChanged(SensorEvent event) {
             if (event.values.length == 0) {
@@ -458,7 +470,14 @@
                 if (DozeMachine.DEBUG) {
                     Log.d(TAG, "ProxCheck: Event: value=" + event.values[0] + " max=" + mMaxRange);
                 }
-                final boolean isNear = event.values[0] < mMaxRange;
+                final boolean isNear;
+                if (mUsingBrightnessSensor) {
+                    // The custom brightness sensor is gated by the proximity sensor and will
+                    // return 0 whenever prox is covered.
+                    isNear = event.values[0] == 0;
+                } else {
+                    isNear = event.values[0] < mMaxRange;
+                }
                 finishWithResult(isNear ? RESULT_NEAR : RESULT_FAR);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 10b48e7..bb6a38e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -207,10 +207,6 @@
         return SystemProperties.get(propName, mContext.getString(resId));
     }
 
-    public boolean getPickupPerformsProxCheck() {
-        return mContext.getResources().getBoolean(R.bool.doze_pickup_performs_proximity_check);
-    }
-
     public int getPulseVisibleDurationExtended() {
         return 2 * getPulseVisibleDuration();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index 2ed0970..0c124ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -37,7 +37,6 @@
         when(params.getPulseOnSigMotion()).thenReturn(false);
         when(params.getPickupVibrationThreshold()).thenReturn(0);
         when(params.getProxCheckBeforePulse()).thenReturn(true);
-        when(params.getPickupPerformsProxCheck()).thenReturn(true);
         when(params.getPolicy()).thenReturn(mock(AlwaysOnDisplayPolicy.class));
         when(params.doubleTapReportsTouchCoordinates()).thenReturn(false);
         when(params.getDisplayNeedsBlanking()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 7df45a3..cd6d1e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -19,7 +19,6 @@
 import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -79,8 +78,6 @@
     private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
     @Mock
     private TriggerSensor mTriggerSensor;
-    @Mock
-    private TriggerSensor mProxGatedTriggerSensor;
     private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
     private TestableLooper mTestableLooper;
     private DozeSensors mDozeSensors;
@@ -88,7 +85,6 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        when(mProxGatedTriggerSensor.performsProxCheck()).thenReturn(true);
         mTestableLooper = TestableLooper.get(this);
         when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L);
         when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
@@ -106,14 +102,14 @@
         mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class));
         mTestableLooper.processAllMessages();
         verify(mCallback).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
-                anyBoolean(), anyFloat(), anyFloat(), eq(null));
+                anyFloat(), anyFloat(), eq(null));
 
         mDozeSensors.requestTemporaryDisable();
         reset(mCallback);
         mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class));
         mTestableLooper.processAllMessages();
         verify(mCallback, never()).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
-                anyBoolean(), anyFloat(), anyFloat(), eq(null));
+                anyFloat(), anyFloat(), eq(null));
     }
 
     @Test
@@ -132,20 +128,17 @@
     }
 
     @Test
-    public void testSetPaused_onlyPausesNonGatedSensors() {
+    public void testSetPaused_doesntPause_sensors() {
         mDozeSensors.setListening(true);
         verify(mTriggerSensor).setListening(eq(true));
-        verify(mProxGatedTriggerSensor).setListening(eq(true));
 
-        clearInvocations(mTriggerSensor, mProxGatedTriggerSensor);
+        clearInvocations(mTriggerSensor);
         mDozeSensors.setPaused(true);
-        verify(mTriggerSensor).setListening(eq(false));
-        verify(mProxGatedTriggerSensor).setListening(eq(true));
-
-        clearInvocations(mTriggerSensor, mProxGatedTriggerSensor);
-        mDozeSensors.setPaused(false);
         verify(mTriggerSensor).setListening(eq(true));
-        verify(mProxGatedTriggerSensor).setListening(eq(true));
+
+        clearInvocations(mTriggerSensor);
+        mDozeSensors.setListening(false);
+        verify(mTriggerSensor).setListening(eq(false));
     }
 
     private class TestableDozeSensors extends DozeSensors {
@@ -161,7 +154,7 @@
                     mWakeLockScreenListener = (PluginSensor) sensor;
                 }
             }
-            mSensors = new TriggerSensor[] {mTriggerSensor, mProxGatedTriggerSensor};
+            mSensors = new TriggerSensor[] {mTriggerSensor};
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index d464223..e190f99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -20,7 +20,6 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -134,28 +133,4 @@
         mTriggers.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.FINISH);
         verify(mDockManagerFake).removeListener(any());
     }
-
-    @Test
-    public void testOnSensor_whenUndockedWithNearAndDoubleTapScreen_shouldNotWakeUp() {
-        mSensors.getMockProximitySensor().sendProximityResult(false /* far */);
-
-        mTriggers.onSensor(DozeLog.REASON_SENSOR_DOUBLE_TAP,
-                false /* sensorPerformedProxCheck */, 50 /* screenX */, 50 /* screenY */,
-                null /* rawValues */);
-        verify(mMachine, never()).wakeUp();
-    }
-
-    @Test
-    public void testOnSensor_whenDockedWithNearAndDoubleTapScreen_shouldWakeUp() {
-        doReturn(true).when(mDockManagerFake).isDocked();
-        doReturn(true).when(mParameters).getDisplayNeedsBlanking();
-        mSensors.getMockProximitySensor().sendProximityResult(false /* far */);
-
-        mTriggers.onSensor(DozeLog.REASON_SENSOR_DOUBLE_TAP,
-                false /* sensorPerformedProxCheck */, 50 /* screenX */, 50 /* screenY */,
-                null /* rawValues */);
-
-        verify(mHost).setAodDimmingScrim(eq(1f));
-        verify(mMachine).wakeUp();
-    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 97fd02f..500a242 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -69,6 +69,7 @@
     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;
+    private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4;
 
     // Special ID used to indicate that given vote is to be applied globally, rather than to a
     // specific display.
@@ -440,23 +441,48 @@
                     mSettingsObserver.onDeviceConfigDefaultPeakRefreshRateChanged(
                             defaultPeakRefreshRate);
                     break;
+
+                case MSG_REFRESH_RATE_IN_ZONE_CHANGED:
+                    int refreshRateInZone = msg.arg1;
+                    mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged(
+                            refreshRateInZone);
+                    break;
             }
         }
     }
 
     private static final class Vote {
-        // We split the app request into two priorities in case we can satisfy one desire without
-        // the other.
-        public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 0;
-        public static final int PRIORITY_APP_REQUEST_SIZE = 1;
-        public static final int PRIORITY_USER_SETTING_REFRESH_RATE = 2;
-        public static final int PRIORITY_LOW_BRIGHTNESS = 3;
-        public static final int PRIORITY_LOW_POWER_MODE = 4;
+        // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
+        // If the higher voters result is a range, it will fix the rate to a single choice.
+        // It's used to avoid rate switch in certain conditions.
+        public static final int PRIORITY_LOW_BRIGHTNESS = 0;
+
+        // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
+        // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
+        public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 1;
+
+        // We split the app request into different priorities in case we can satisfy one desire
+        // without the other.
+
+        // Application can specify preferred refresh rate with below attrs.
+        // @see android.view.WindowManager.LayoutParams#preferredRefreshRate
+        // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
+        // System also forces some apps like blacklisted app to run at a lower refresh rate.
+        // @see android.R.array#config_highRefreshRateBlacklist
+        public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 2;
+        public static final int PRIORITY_APP_REQUEST_SIZE = 3;
+
+        // SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest
+        // of low priority voters. It votes [0, max(PEAK, MIN)]
+        public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 4;
+
+        // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
+        public static final int PRIORITY_LOW_POWER_MODE = 5;
 
         // Whenever a new priority is added, remember to update MIN_PRIORITY and/or MAX_PRIORITY as
         // appropriate, as well as priorityToString.
 
-        public static final int MIN_PRIORITY = PRIORITY_APP_REQUEST_REFRESH_RATE;
+        public static final int MIN_PRIORITY = PRIORITY_LOW_BRIGHTNESS;
         public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;
 
         /**
@@ -500,12 +526,16 @@
 
         public static String priorityToString(int priority) {
             switch (priority) {
+                case PRIORITY_LOW_BRIGHTNESS:
+                    return "PRIORITY_LOW_BRIGHTNESS";
+                case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
+                    return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
                 case PRIORITY_APP_REQUEST_REFRESH_RATE:
                     return "PRIORITY_APP_REQUEST_REFRESH_RATE";
                 case PRIORITY_APP_REQUEST_SIZE:
                     return "PRIORITY_APP_REQUEST_SIZE";
-                case PRIORITY_USER_SETTING_REFRESH_RATE:
-                    return "PRIORITY_USER_SETTING_REFRESH_RATE";
+                case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
+                    return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
                 case PRIORITY_LOW_POWER_MODE:
                     return "PRIORITY_LOW_POWER_MODE";
                 default:
@@ -608,12 +638,11 @@
             float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
                     Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate);
 
-            if (peakRefreshRate < minRefreshRate) {
-                peakRefreshRate = minRefreshRate;
-            }
+            updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE,
+                    Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate)));
+            updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
+                    Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY));
 
-            Vote vote = Vote.forRefreshRates(minRefreshRate, peakRefreshRate);
-            updateVoteLocked(Vote.PRIORITY_USER_SETTING_REFRESH_RATE, vote);
             mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, peakRefreshRate);
         }
 
@@ -655,6 +684,7 @@
                 refreshRateVote = null;
                 sizeVote = null;
             }
+
             updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote);
             updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
             return;
@@ -799,6 +829,8 @@
         private boolean mRefreshRateChangeable = false;
         private boolean mLowPowerModeEnabled = false;
 
+        private int mRefreshRateInZone;
+
         BrightnessObserver(Context context, Handler handler) {
             super(handler);
             mContext = context;
@@ -816,6 +848,7 @@
 
         public void observe(SensorManager sensorManager) {
             mSensorManager = sensorManager;
+
             // DeviceConfig is accessible after system ready.
             int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds();
             int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds();
@@ -825,6 +858,8 @@
                 mDisplayBrightnessThresholds = brightnessThresholds;
                 mAmbientBrightnessThresholds = ambientThresholds;
             }
+
+            mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone();
             restartObserver();
             mDeviceConfigDisplaySettings.startListening();
         }
@@ -864,8 +899,16 @@
             restartObserver();
         }
 
+        public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) {
+            if (refreshRate != mRefreshRateInZone) {
+                mRefreshRateInZone = refreshRate;
+                restartObserver();
+            }
+        }
+
         public void dumpLocked(PrintWriter pw) {
             pw.println("  BrightnessObserver");
+            pw.println("    mRefreshRateInZone: " + mRefreshRateInZone);
 
             for (int d: mDisplayBrightnessThresholds) {
                 pw.println("    mDisplayBrightnessThreshold: " + d);
@@ -952,6 +995,10 @@
          * to value changes.
          */
         private boolean checkShouldObserve(int[] a) {
+            if (mRefreshRateInZone <= 0) {
+                return false;
+            }
+
             for (int d: a) {
                 if (d >= 0) {
                     return true;
@@ -961,37 +1008,42 @@
             return false;
         }
 
+        private boolean isInsideZone(int brightness, float lux) {
+            for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
+                int disp = mDisplayBrightnessThresholds[i];
+                int ambi = mAmbientBrightnessThresholds[i];
+
+                if (disp >= 0 && ambi >= 0) {
+                    if (brightness <= disp && mAmbientLux <= ambi) {
+                        return true;
+                    }
+                } else if (disp >= 0) {
+                    if (brightness <= disp) {
+                        return true;
+                    }
+                } else if (ambi >= 0) {
+                    if (mAmbientLux <= ambi) {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
         private void onBrightnessChangedLocked() {
             int brightness = Settings.System.getInt(mContext.getContentResolver(),
                     Settings.System.SCREEN_BRIGHTNESS, -1);
 
             Vote vote = null;
-            for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
-                int disp = mDisplayBrightnessThresholds[i];
-                int ambi = mAmbientBrightnessThresholds[i];
-
-                if (disp >= 0 && ambi >= 0) {
-                    if (brightness <= disp && mAmbientLux <= ambi) {
-                        vote = Vote.forRefreshRates(0f, 60f);
-                    }
-                } else if (disp >= 0) {
-                    if (brightness <= disp) {
-                        vote = Vote.forRefreshRates(0f, 60f);
-                    }
-                } else if (ambi >= 0) {
-                    if (mAmbientLux <= ambi) {
-                        vote = Vote.forRefreshRates(0f, 60f);
-                    }
-                }
-
-                if (vote != null) {
-                    break;
-                }
+            boolean insideZone = isInsideZone(brightness, mAmbientLux);
+            if (insideZone) {
+                vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone);
             }
 
             if (DEBUG) {
                 Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " +  mAmbientLux +
-                        (vote != null ? " 60hz only" : " no refresh rate limit"));
+                        ", Vote " + vote);
             }
             updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
         }
@@ -1133,7 +1185,6 @@
     }
 
     private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
-
         public DeviceConfigDisplaySettings() {
         }
 
@@ -1147,7 +1198,8 @@
          */
         public int[] getBrightnessThresholds() {
             return getIntArrayProperty(
-                    DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_BRIGHTNESS_THRESHOLDS);
+                    DisplayManager.DeviceConfig.
+                            KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS);
         }
 
         /*
@@ -1155,7 +1207,8 @@
          */
         public int[] getAmbientThresholds() {
             return getIntArrayProperty(
-                    DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_AMBIENT_THRESHOLDS);
+                    DisplayManager.DeviceConfig.
+                            KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS);
         }
 
         /*
@@ -1172,17 +1225,32 @@
             return defaultPeakRefreshRate;
         }
 
+        public int getRefreshRateInZone() {
+            int defaultRefreshRateInZone = mContext.getResources().getInteger(
+                    R.integer.config_defaultRefreshRateInZone);
+
+            int refreshRate = DeviceConfig.getInt(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
+                    defaultRefreshRateInZone);
+
+            return refreshRate;
+        }
+
         @Override
         public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
             int[] brightnessThresholds = getBrightnessThresholds();
             int[] ambientThresholds = getAmbientThresholds();
             Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
+            int refreshRateInZone = getRefreshRateInZone();
 
             mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED,
                     new Pair<int[], int[]>(brightnessThresholds, ambientThresholds))
                     .sendToTarget();
             mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
                     defaultPeakRefreshRate).sendToTarget();
+            mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone,
+                    0).sendToTarget();
         }
 
         private int[] getIntArrayProperty(String prop) {
diff --git a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
index b33b68a7..271db289 100644
--- a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
+++ b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
@@ -110,7 +110,9 @@
     private class OnPropertyChangedListener implements DeviceConfig.OnPropertyChangedListener {
         public void onPropertyChanged(@NonNull String namespace, @NonNull String name,
                 @Nullable String value) {
-            updateBlacklist(value);
+            if (KEY_HIGH_REFRESH_RATE_BLACKLIST.equals(name)) {
+                updateBlacklist(value);
+            }
         }
     }
 }