Merge "Import translations. DO NOT MERGE ANYWHERE" into rvc-qpr-dev
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index decf053..7dbb570 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -235,7 +235,7 @@
      * @see StreamConfigurationMap#getOutputFormats()
      * @see StreamConfigurationMap#getOutputSizes(int)
      * @see StreamConfigurationMap#getOutputSizes(Class)
-     * @deprecated Please use @{link
+     * @deprecated Please use {@link
      *      #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
      *      full set of configuration options available.
      */
@@ -250,7 +250,7 @@
      *
      * @see #createCaptureSession
      * @see OutputConfiguration
-     * @deprecated Please use @{link
+     * @deprecated Please use {@link
      *      #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
      *      full set of configuration options available.
      */
@@ -286,7 +286,7 @@
      * @see StreamConfigurationMap#getOutputSizes
      * @see android.media.ImageWriter
      * @see android.media.ImageReader
-     * @deprecated Please use @{link
+     * @deprecated Please use {@link
      *      #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
      *      full set of configuration options available.
      */
@@ -303,7 +303,7 @@
      *
      * @see #createReprocessableCaptureSession
      * @see OutputConfiguration
-     * @deprecated Please use @{link
+     * @deprecated Please use {@link
      *      #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
      *      full set of configuration options available.
      */
@@ -341,7 +341,7 @@
      * @see CameraCaptureSession#captureBurst
      * @see CameraCaptureSession#setRepeatingBurst
      * @see CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList
-     * @deprecated Please use @{link
+     * @deprecated Please use {@link
      *      #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
      *      full set of configuration options available.
      */
@@ -417,7 +417,7 @@
      * @see #createReprocessableCaptureSession
      * @see CameraCaptureSession
      * @see OutputConfiguration
-     * @deprecated Please use @{link
+     * @deprecated Please use {@link
      *      #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
      *      full set of configuration options available.
      * @hide
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index c1ba209..0fa4ca8 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -875,12 +875,52 @@
     public interface DeviceConfig {
 
         /**
-         * Key for refresh rate in the zone defined by thresholds.
+         * Key for refresh rate in the low zone defined by thresholds.
          *
+         * Note that the name and value don't match because they were added before we had a high
+         * zone to consider.
          * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
          * @see android.R.integer#config_defaultZoneBehavior
          */
-        String KEY_REFRESH_RATE_IN_ZONE = "refresh_rate_in_zone";
+        String KEY_REFRESH_RATE_IN_LOW_ZONE = "refresh_rate_in_zone";
+
+        /**
+         * Key for accessing the low 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]).
+         *
+         * Note that the name and value don't match because they were added before we had a high
+         * zone to consider.
+         *
+         * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+         * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
+         * @hide
+         */
+        String KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS =
+                "peak_refresh_rate_brightness_thresholds";
+
+        /**
+         * Key for accessing the low 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.
+         *
+         * Note that the name and value don't match because they were added before we had a high
+         * zone to consider.
+         *
+         * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+         * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
+         * @hide
+         */
+        String KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS =
+                "peak_refresh_rate_ambient_thresholds";
+        /**
+         * Key for refresh rate in the high zone defined by thresholds.
+         *
+         * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+         * @see android.R.integer#config_fixedRefreshRateInHighZone
+         */
+        String KEY_REFRESH_RATE_IN_HIGH_ZONE = "refresh_rate_in_high_zone";
 
         /**
          * Key for accessing the display brightness thresholds for the configured refresh rate zone.
@@ -888,11 +928,11 @@
          * 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
+         * @see android.R.array#config_brightnessHighThresholdsOfFixedRefreshRate
          * @hide
          */
-        String KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS =
-                "peak_refresh_rate_brightness_thresholds";
+        String KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS =
+                "fixed_refresh_rate_high_display_brightness_thresholds";
 
         /**
          * Key for accessing the ambient brightness thresholds for the configured refresh rate zone.
@@ -900,12 +940,11 @@
          * thresholds of the zone, respectively, in lux.
          *
          * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
-         * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
+         * @see android.R.array#config_ambientHighThresholdsOfFixedRefreshRate
          * @hide
          */
-        String KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS =
-                "peak_refresh_rate_ambient_thresholds";
-
+        String KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS =
+                "fixed_refresh_rate_high_ambient_brightness_thresholds";
         /**
          * Key for default peak refresh rate
          *
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 2cfd6105..8cccea8 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2688,4 +2688,7 @@
     // CATEGORY: SETTINGS
     // OS: R QPR
     ADAPTIVE_CONNECTIVITY_CATEGORY = 1850;
+
+    // OS: R QPR2
+    BLUETOOTH_PAIRING_RECEIVER = 1851;
 }
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 17db69c..daec63a 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -923,7 +923,7 @@
     <string name="granularity_label_link" msgid="9007852307112046526">"link"</string>
     <string name="granularity_label_line" msgid="376204904280620221">"red"</string>
     <string name="factorytest_failed" msgid="3190979160945298006">"Fabričko testiranje nije uspelo"</string>
-    <string name="factorytest_not_system" msgid="5658160199925519869">"Radnja FACTORY_TEST je podržana samo za pakete instalirane u direktorijumu /system/app."</string>
+    <string name="factorytest_not_system" msgid="5658160199925519869">"Radnja FACTORY_TEST je podržana samo za pakete instalirane u folderu /system/app."</string>
     <string name="factorytest_no_action" msgid="339252838115675515">"Nije pronađen nijedan paket koji obezbeđuje radnju FACTORY_TEST."</string>
     <string name="factorytest_reboot" msgid="2050147445567257365">"Restartuj"</string>
     <string name="js_dialog_title" msgid="7464775045615023241">"Na stranici na adresi „<xliff:g id="TITLE">%s</xliff:g>“ piše:"</string>
@@ -2038,7 +2038,7 @@
     <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Baterija telefona je dovoljno napunjena. Funkcije više nisu ograničene."</string>
     <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Baterija tableta je dovoljno napunjena. Funkcije više nisu ograničene."</string>
     <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Baterija uređaja je dovoljno napunjena. Funkcije više nisu ograničene."</string>
-    <string name="mime_type_folder" msgid="2203536499348787650">"Direktorijum"</string>
+    <string name="mime_type_folder" msgid="2203536499348787650">"Folder"</string>
     <string name="mime_type_apk" msgid="3168784749499623902">"Android aplikacija"</string>
     <string name="mime_type_generic" msgid="4606589110116560228">"Datoteka"</string>
     <string name="mime_type_generic_ext" msgid="9220220924380909486">"<xliff:g id="EXTENSION">%1$s</xliff:g> datoteka"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index c0ec8e0..5e52506 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -492,7 +492,7 @@
     <string name="permdesc_changeNetworkState" msgid="649341947816898736">"Sarearen konexioaren egoera aldatzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_changeTetherState" msgid="9079611809931863861">"aldatu telefono bidezko konektagarritasuna"</string>
     <string name="permdesc_changeTetherState" msgid="3025129606422533085">"Partekatutako Interneterako konexioaren egoera aldatzeko baimena ematen die aplikazioei."</string>
-    <string name="permlab_accessWifiState" msgid="5552488500317911052">"ikusi wifi bidezko konexioak"</string>
+    <string name="permlab_accessWifiState" msgid="5552488500317911052">"ikusi wifi-konexioak"</string>
     <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Wi-Fi sareei buruzko informazioa ikusteko baimena ematen die aplikazioei, adibidez, Wi-Fi konexioa aktibatuta dagoen eta konektatutako Wi-Fi gailuen izenak zein diren."</string>
     <string name="permlab_changeWifiState" msgid="7947824109713181554">"konektatu wifira edo deskonektatu bertatik"</string>
     <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Wi-Fi sarbide-puntuetara konektatzeko edo haietatik deskonektatzeko baimena ematen die aplikazioei, baita Wi-Fi sareen gailu-konfigurazioari aldaketak egitekoa ere."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c9392fb..fc91c47 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1815,7 +1815,7 @@
     <string name="package_updated_device_owner" msgid="7560272363805506941">"Mis à jour par votre administrateur"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Supprimé par votre administrateur"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
-    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Pour prolonger l\'autonomie de la batterie, l\'économiseur de batterie :\n\n·• active le thème sombre ;\n·• désactive ou restreint les activités en arrière-plan, certains effets visuels et d\'autres fonctionnalités, comme \"Hey Google\".\n\n"<annotation id="url">"En savoir plus"</annotation></string>
+    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Pour prolonger l\'autonomie de la batterie, l\'économiseur de batterie :\n\n • active le thème sombre ;\n • désactive ou restreint les activités en arrière-plan, certains effets visuels et d\'autres fonctionnalités, comme \"Hey Google\".\n\n"<annotation id="url">"En savoir plus"</annotation></string>
     <string name="battery_saver_description" msgid="6794188153647295212">"Pour prolonger l\'autonomie de la batterie, l\'économiseur de batterie :\n\n• active le thème sombre ;\n • désactive ou restreint les activités en arrière-plan, certains effets visuels et d\'autres fonctionnalités, comme \"Hey Google\"."</string>
     <string name="data_saver_description" msgid="4995164271550590517">"Pour réduire la consommation de données, l\'économiseur de données empêche certaines applications d\'envoyer ou de recevoir des données en arrière-plan. Ainsi, les applications que vous utilisez peuvent toujours accéder aux données, mais pas en permanence. Par exemple, il se peut que les images ne s\'affichent pas tant que vous n\'appuyez pas dessus."</string>
     <string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'économiseur de données ?"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index f5e7b41..95e1a48 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1792,8 +1792,8 @@
     <string name="package_updated_device_owner" msgid="7560272363805506941">"Aggiornato dall\'amministratore"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminato dall\'amministratore"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
-    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Per estendere la durata della batteria, la funzionalità di risparmio energetico:\n\n• Attiva il tema scuro\n• Disattiva o limita le attività in background, alcuni effetti visivi e altre funzionalità come \"Ok Google\"\n\n"<annotation id="url">"Ulteriori informazioni"</annotation></string>
-    <string name="battery_saver_description" msgid="6794188153647295212">"Per estendere la durata della batteria, la funzionalità di risparmio energetico:\n\n• Attiva il tema scuro\n• Disattiva o limita le attività in background, alcuni effetti visivi e altre funzionalità come \"Ok Google\""</string>
+    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Per estendere la durata della batteria, la funzionalità di risparmio energetico:\n\n• Attiva il tema scuro\n• Disattiva o limita le attività in background, alcuni effetti visivi e altre funzionalità come \"Hey Google\"\n\n"<annotation id="url">"Scopri di più"</annotation></string>
+    <string name="battery_saver_description" msgid="6794188153647295212">"Per estendere la durata della batteria, la funzionalità di risparmio energetico:\n\n• Attiva il tema scuro\n• Disattiva o limita le attività in background, alcuni effetti visivi e altre funzionalità come \"Hey Google\""</string>
     <string name="data_saver_description" msgid="4995164271550590517">"Per contribuire a ridurre l\'utilizzo dei dati, la funzione Risparmio dati impedisce ad alcune app di inviare o ricevere dati in background. Un\'app in uso può accedere ai dati, ma potrebbe farlo con meno frequenza. Esempio: le immagini non vengono visualizzate finché non le tocchi."</string>
     <string name="data_saver_enable_title" msgid="7080620065745260137">"Attivare Risparmio dati?"</string>
     <string name="data_saver_enable_button" msgid="4399405762586419726">"Attiva"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 8168b53..d9ea686 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1792,8 +1792,8 @@
     <string name="package_updated_device_owner" msgid="7560272363805506941">"Администраторуңуз жаңыртып койгон"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Администраторуңуз жок кылып салган"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ЖАРАЙТ"</string>
-    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Батареянын мөөнөтүн узартуу үчүн Батареяны үнөмдөгүч режими төмөнкүлөрдү аткарат:\n\n• Караңгы теманы күйгүзөт\n• Фондогу аракеттерди, айрым визуалдык эффекттерди жана \"Окей Google\" сыяктуу башка функцияларды өчүрөт же чектейт\n\n"<annotation id="url">"Кеңири маалымат"</annotation></string>
-    <string name="battery_saver_description" msgid="6794188153647295212">"Батареянын мөөнөтүн узартуу үчүн Батареяны үнөмдөгүч режими:\n\n• Караңгы теманы күйгүзөт\n• Фондогу аракеттерди, айрым визуалдык эффекттерди жана \"Окей Google\" сыяктуу башка функцияларды өчүрөт же чектейт"</string>
+    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Батареяны көбүрөөк убакытка жеткирүү үчүн Батареяны үнөмдөгүч режиминде:\n\n• Караңгы тема күйгүзүлөт\n• Фондогу аракеттерди, айрым визуалдык эффекттерди жана \"Окей Google\" сыяктуу башка функцияларды өчүрөт же чектейт\n\n"<annotation id="url">"Кеңири маалымат"</annotation></string>
+    <string name="battery_saver_description" msgid="6794188153647295212">"Батареяны көбүрөөк убакытка жеткирүү үчүн Батареяны үнөмдөгүч режими:\n\n• Караңгы тема күйгүзүлөт\n• Фондогу аракеттерди, айрым визуалдык эффекттерди жана \"Окей Google\" сыяктуу башка функцияларды өчүрөт же чектейт"</string>
     <string name="data_saver_description" msgid="4995164271550590517">"Трафикти үнөмдөө режиминде айрым колдонмолор дайын-даректерди фондо өткөрө алышпайт. Учурда сиз пайдаланып жаткан колдонмо дайын-даректерди жөнөтүп/ала алат, бирок адаттагыдан азыраак өткөргөндүктөн, анын айрым функциялары талаптагыдай иштебей коюшу мүмкүн. Мисалы, сүрөттөр басылмайынча жүктөлбөйт."</string>
     <string name="data_saver_enable_title" msgid="7080620065745260137">"Трафикти үнөмдөө режимин иштетесизби?"</string>
     <string name="data_saver_enable_button" msgid="4399405762586419726">"Күйгүзүү"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index c69334b..a87c7e7 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -48,7 +48,7 @@
     <string name="invalidPin" msgid="7542498253319440408">"4-8 тооноос бүтэх PIN-г бичнэ үү."</string>
     <string name="invalidPuk" msgid="8831151490931907083">"8-с цөөнгүй тооноос бүтэх PUK-г бичнэ үү."</string>
     <string name="needPuk" msgid="7321876090152422918">"SIM картны PUK-түгжигдсэн. Тайлах бол PUK кодыг бичнэ үү."</string>
-    <string name="needPuk2" msgid="7032612093451537186">"SIM картын хаалтыг болиулах бол PUK2-г бичнэ үү."</string>
+    <string name="needPuk2" msgid="7032612093451537186">"SIM картыг блокоос гаргах бол PUK2-г бичнэ үү."</string>
     <string name="enablePin" msgid="2543771964137091212">"Амжилтгүй боллоо, СИМ/РҮИМ түгжээг идэвхжүүлнэ үү."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
       <item quantity="other">Таны СИМ түгжигдэхээс өмнө танд <xliff:g id="NUMBER_1">%d</xliff:g> оролдлого хийх боломж үлдлээ. </item>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 393156b..ef599a1 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1838,7 +1838,7 @@
     <string name="package_updated_device_owner" msgid="7560272363805506941">"Zaktualizowany przez administratora"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Usunięty przez administratora"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
-    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Aby wydłużyć czas pracy na baterii, funkcja Oszczędzanie baterii:\n\n•włącza tryb ciemny,\n• wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne oraz inne funkcje, np. „OK Google”.\n\n"<annotation id="url">"Więcej informacji"</annotation></string>
+    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Aby wydłużyć czas pracy na baterii, funkcja Oszczędzanie baterii:\n\n• włącza tryb ciemny,\n• wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne oraz inne funkcje, np. „OK Google”.\n\n"<annotation id="url">"Więcej informacji"</annotation></string>
     <string name="battery_saver_description" msgid="6794188153647295212">"Aby wydłużyć czas pracy na baterii, Oszczędzanie baterii:\n\n• włącza tryb ciemny,\n• wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne oraz inne funkcje, np. „OK Google”."</string>
     <string name="data_saver_description" msgid="4995164271550590517">"Oszczędzanie danych uniemożliwia niektórym aplikacjom wysyłanie i odbieranie danych w tle, zmniejszając w ten sposób ich użycie. Aplikacja, z której w tej chwili korzystasz, może uzyskiwać dostęp do danych, ale rzadziej. Może to powodować, że obrazy będą się wyświetlać dopiero po kliknięciu."</string>
     <string name="data_saver_enable_title" msgid="7080620065745260137">"Włączyć Oszczędzanie danych?"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 9554712..32cfb18 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1838,7 +1838,7 @@
     <string name="package_updated_device_owner" msgid="7560272363805506941">"Posodobil skrbnik"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisal skrbnik"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"V redu"</string>
-    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Funkcija varčevanja z energijo baterije podaljša čas delovanja baterije tako:\n\n•vklopi temno temo,\n•izklopi ali omeji dejavnost v ozadju, nekatere vizualne učinke in druge funkcije, kot je »Hey Google«.\n\n"<annotation id="url">"Več o tem"</annotation></string>
+    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Funkcija varčevanja z energijo baterije podaljša čas delovanja baterije tako:\n\n• vklopi temno temo,\n• izklopi ali omeji dejavnost v ozadju, nekatere vizualne učinke in druge funkcije, kot je »Hey Google«.\n\n"<annotation id="url">"Več o tem"</annotation></string>
     <string name="battery_saver_description" msgid="6794188153647295212">"Funkcija varčevanja z energijo baterije podaljša čas delovanja baterije tako:\n\n• vklopi temno temo;\n• izklopi ali omeji dejavnost v ozadju, nekatere vizualne učinke in druge funkcije, kot je »Hey Google«."</string>
     <string name="data_saver_description" msgid="4995164271550590517">"Zaradi zmanjševanja prenesene količine podatkov funkcija varčevanja s podatki nekaterim aplikacijam preprečuje, da bi v ozadju pošiljale ali prejemale podatke. Aplikacija, ki jo trenutno uporabljate, lahko prenaša podatke, vendar to morda počne manj pogosto. To na primer pomeni, da se slike ne prikažejo, dokler se jih ne dotaknete."</string>
     <string name="data_saver_enable_title" msgid="7080620065745260137">"Vklop varčevanja s podatki?"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 329a6f2..d2e7865 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -923,7 +923,7 @@
     <string name="granularity_label_link" msgid="9007852307112046526">"линк"</string>
     <string name="granularity_label_line" msgid="376204904280620221">"ред"</string>
     <string name="factorytest_failed" msgid="3190979160945298006">"Фабричко тестирање није успело"</string>
-    <string name="factorytest_not_system" msgid="5658160199925519869">"Радња FACTORY_TEST је подржана само за пакете инсталиране у директоријуму /system/app."</string>
+    <string name="factorytest_not_system" msgid="5658160199925519869">"Радња FACTORY_TEST је подржана само за пакете инсталиране у фолдеру /system/app."</string>
     <string name="factorytest_no_action" msgid="339252838115675515">"Није пронађен ниједан пакет који обезбеђује радњу FACTORY_TEST."</string>
     <string name="factorytest_reboot" msgid="2050147445567257365">"Рестартуј"</string>
     <string name="js_dialog_title" msgid="7464775045615023241">"На страници на адреси „<xliff:g id="TITLE">%s</xliff:g>“ пише:"</string>
@@ -2038,7 +2038,7 @@
     <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Батерија телефона је довољно напуњена. Функције више нису ограничене."</string>
     <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Батерија таблета је довољно напуњена. Функције више нису ограничене."</string>
     <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Батерија уређаја је довољно напуњена. Функције више нису ограничене."</string>
-    <string name="mime_type_folder" msgid="2203536499348787650">"Директоријум"</string>
+    <string name="mime_type_folder" msgid="2203536499348787650">"Фолдер"</string>
     <string name="mime_type_apk" msgid="3168784749499623902">"Android апликација"</string>
     <string name="mime_type_generic" msgid="4606589110116560228">"Датотека"</string>
     <string name="mime_type_generic_ext" msgid="9220220924380909486">"<xliff:g id="EXTENSION">%1$s</xliff:g> датотека"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 2b67ed9..04e1ab2 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1885,7 +1885,7 @@
     <string name="app_suspended_title" msgid="888873445010322650">"Ilova ishlamayapti"</string>
     <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ishlamayapti. Uning ishlashini <xliff:g id="APP_NAME_1">%2$s</xliff:g> cheklamoqda."</string>
     <string name="app_suspended_more_details" msgid="211260942831587014">"Batafsil"</string>
-    <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Ilovani ishga tushirish"</string>
+    <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Ilovani pauzadan chiqarish"</string>
     <string name="work_mode_off_title" msgid="5503291976647976560">"Ish profili yoqilsinmi?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Ishga oid ilovalar, bildirishnomalar, ma’lumotlar va boshqa ish profili imkoniyatlari yoqiladi"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Yoqish"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 550601a..5f02eb6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4138,6 +4138,35 @@
          If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
     <integer name="config_defaultRefreshRateInZone">0</integer>
 
+    <!-- The display uses different gamma curves for different refresh rates. It's hard for panel
+         vendor to tune the curves to have exact same brightness for different refresh rate. So
+         flicker could be observed at switch time. The issue can be observed on the screen with
+         even full white content at the high brightness. To prevent flickering, we support fixed
+         refresh rates if the display and ambient brightness are equal to or above the provided
+         thresholds. You can define multiple threshold levels as higher brightness environments
+         may have lower display brightness requirements for the flickering is visible. And the
+         high brightness environment could have higher threshold.
+         For example, fixed refresh rate if
+             display brightness >= disp0 && ambient brightness >= amb0
+             || display brightness >= disp1 && ambient brightness >= amb1 -->
+    <integer-array translatable="false" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate">
+         <!--
+           <item>disp0</item>
+           <item>disp1</item>
+        -->
+    </integer-array>
+
+    <integer-array translatable="false" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate">
+         <!--
+           <item>amb0</item>
+           <item>amb1</item>
+        -->
+    </integer-array>
+
+    <!-- Default refresh rate in the high zone defined by brightness and ambient thresholds.
+         If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
+    <integer name="config_fixedRefreshRateInHighZone">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 06f357e..b80e8e1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3783,6 +3783,11 @@
   <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
   <java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" />
 
+  <!-- For fixed refresh rate displays in high brightness-->
+  <java-symbol type="integer" name="config_fixedRefreshRateInHighZone" />
+  <java-symbol type="array" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate" />
+  <java-symbol type="array" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate" />
+
   <!-- For Auto-Brightness -->
   <java-symbol type="string" name="config_displayLightSensorType" />
 
diff --git a/packages/SettingsLib/HelpUtils/res/values-ar/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ar/strings.xml
index 7ab3abc..5b12fc5 100644
--- a/packages/SettingsLib/HelpUtils/res/values-ar/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values-ar/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="help_feedback_label" msgid="7106780063063027882">"المساعدة والتعليقات"</string>
+    <string name="help_feedback_label" msgid="7106780063063027882">"المساعدة والملاحظات والآراء"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index bf936cb..1d7103e 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -38,8 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • چارج ہو رہا ہے"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • تیزی سے چارج ہو رہا ہے"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • آہستہ چارج ہو رہا ہے"</string>
-    <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) -->
-    <skip />
+    <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • بیٹری عارضی طور پر محدود ہے"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"اپنا چارجر منسلک کریں۔"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"غیر مقفل کرنے کیلئے مینو دبائیں۔"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"نیٹ ورک مقفل ہو گیا"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 8c9a29b..59b51b4 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -951,7 +951,7 @@
     <string name="notification_channel_general" msgid="4384774889645929705">"সাধাৰণ বার্তাসমূহ"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"সঞ্চয়াগাৰ"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"ইংগিতবোৰ"</string>
-    <string name="instant_apps" msgid="8337185853050247304">"তাৎক্ষণিক এপসমূহ"</string>
+    <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
     <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> চলি আছে"</string>
     <string name="instant_apps_message" msgid="6112428971833011754">"ইনষ্ট\'ল নকৰাকৈয়েই এপটো খোলা হৈছে।"</string>
     <string name="instant_apps_message_with_help" msgid="1816952263531203932">"ইনষ্ট\'ল নকৰাকৈয়েই এপটো খোলা হৈছে। অধিক জানিবলৈ টিপক।"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 7c44950a..28e4d0b 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -951,7 +951,7 @@
     <string name="notification_channel_general" msgid="4384774889645929705">"সাধারণ বার্তাগুলি"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"স্টোরেজ"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"হিন্ট"</string>
-    <string name="instant_apps" msgid="8337185853050247304">"ইনস্ট্যান্ট অ্যাপ"</string>
+    <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
     <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> চলছে"</string>
     <string name="instant_apps_message" msgid="6112428971833011754">"অ্যাপটি ইনস্টল না করে চালু করা হয়েছে।"</string>
     <string name="instant_apps_message_with_help" msgid="1816952263531203932">"অ্যাপটি ইনস্টল না করে চালু করা হয়েছে। আরও জানতে ট্যাপ করুন।"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index fcd24f2..203f7ae 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -951,7 +951,7 @@
     <string name="notification_channel_general" msgid="4384774889645929705">"સામાન્ય સંદેશા"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"સ્ટોરેજ"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"હિન્ટ"</string>
-    <string name="instant_apps" msgid="8337185853050247304">"ઝટપટ ઍપ્લિકેશન"</string>
+    <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
     <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> ચાલી રહી છે"</string>
     <string name="instant_apps_message" msgid="6112428971833011754">"ઍપ ઇન્સ્ટૉલ કર્યા વિના ખુલી જાય છે."</string>
     <string name="instant_apps_message_with_help" msgid="1816952263531203932">"ઍપ ઇન્સ્ટૉલ કર્યા વિના ખુલી જાય છે. વધુ જાણવા માટે ટૅપ કરો."</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index df89f45..b9bc9b8 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -953,7 +953,7 @@
     <string name="notification_channel_general" msgid="4384774889645929705">"सामान्य संदेश"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"जगह"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"संकेत"</string>
-    <string name="instant_apps" msgid="8337185853050247304">"झटपट ऐप्लिकेशन"</string>
+    <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
     <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> चल रहा है"</string>
     <string name="instant_apps_message" msgid="6112428971833011754">"ऐप्लिकेशन इंस्टॉल किए बिना ही खुल गया है."</string>
     <string name="instant_apps_message_with_help" msgid="1816952263531203932">"ऐप्लिकेशन इंस्टॉल किए बिना ही खुल गया है. ज़्यादा जानने के लिए टैप करें."</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 3567b1e..50f2149 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -951,7 +951,7 @@
     <string name="notification_channel_general" msgid="4384774889645929705">"ಸಾಮಾನ್ಯ ಸಂದೇಶಗಳು"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"ಸಂಗ್ರಹಣೆ"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"ಸುಳಿವುಗಳು"</string>
-    <string name="instant_apps" msgid="8337185853050247304">"ಇನ್‌ಸ್ಟಂಟ್ ಆ್ಯಪ್‌ಗಳು"</string>
+    <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
     <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> ರನ್ ಆಗುತ್ತಿದೆ"</string>
     <string name="instant_apps_message" msgid="6112428971833011754">"ಇನ್‌ಸ್ಟಾಲ್‌ ಮಾಡದೆ ಆ್ಯಪ್‌ ತೆರೆಯಲಾಗಿದೆ."</string>
     <string name="instant_apps_message_with_help" msgid="1816952263531203932">"ಇನ್‌ಸ್ಟಾಲ್‌ ಮಾಡದೆ ಆ್ಯಪ್‌ ತೆರೆಯಲಾಗಿದೆ. ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index f8ee917..442b402 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -951,7 +951,7 @@
     <string name="notification_channel_general" msgid="4384774889645929705">"പൊതുവായ സന്ദേശങ്ങൾ"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"സ്റ്റോറേജ്"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"സൂചനകൾ"</string>
-    <string name="instant_apps" msgid="8337185853050247304">"ഇൻസ്റ്റന്റ് ആപ്പ്"</string>
+    <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
     <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> റണ്‍ ചെയ്യുന്നു"</string>
     <string name="instant_apps_message" msgid="6112428971833011754">"ഇൻസ്‌റ്റാൾ ചെയ്യാതെ ആപ്പ് തുറന്നു."</string>
     <string name="instant_apps_message_with_help" msgid="1816952263531203932">"ഇൻസ്‌റ്റാൾ ചെയ്യാതെ ആപ്പ് തുറന്നു. കൂടുതലറിയാൻ ടാപ്പ് ചെയ്യുക."</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 160382a..2b524d9 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -951,7 +951,7 @@
     <string name="notification_channel_general" msgid="4384774889645929705">"सर्वसाधारण मेसेज"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"स्टोरेज"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"सूचना"</string>
-    <string name="instant_apps" msgid="8337185853050247304">"इन्सटंट अ‍ॅप्स"</string>
+    <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
     <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> रन होत आहे"</string>
     <string name="instant_apps_message" msgid="6112428971833011754">"इंस्टॉल केल्याशिवाय अ‍ॅप उघडले."</string>
     <string name="instant_apps_message_with_help" msgid="1816952263531203932">"इंस्टॉल केल्याशिवाय अ‍ॅप उघडले. अधिक जाणून घेण्यासाठी टॅप करा."</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 3862516..e5b41cc 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -951,7 +951,7 @@
     <string name="notification_channel_general" msgid="4384774889645929705">"ସାଧାରଣ ମେସେଜ୍"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"ଷ୍ଟୋରେଜ୍‌"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"ହିଣ୍ଟ"</string>
-    <string name="instant_apps" msgid="8337185853050247304">"ଇନଷ୍ଟାଣ୍ଟ ଆପ୍‌"</string>
+    <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
     <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> ଚାଲୁଛି"</string>
     <string name="instant_apps_message" msgid="6112428971833011754">"ଇନ୍‍ଷ୍ଟଲ୍‍ ନହୋଇ ଆପ୍‍ ଖୋଲିଛି।"</string>
     <string name="instant_apps_message_with_help" msgid="1816952263531203932">"ଇନ୍‍ଷ୍ଟଲ୍‍ ନହୋଇ ଆପ୍‍ ଖୋଲିଛି। ଅଧିକ ଜାଣିବା ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ।"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 98c5afb6..4cefcb8 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -951,7 +951,7 @@
     <string name="notification_channel_general" msgid="4384774889645929705">"ਆਮ ਸੁਨੇਹੇ"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"ਸਟੋਰੇਜ"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"ਸੰਕੇਤ"</string>
-    <string name="instant_apps" msgid="8337185853050247304">"ਤਤਕਾਲ ਐਪਾਂ"</string>
+    <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
     <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> ਚੱਲ ਰਹੀ ਹੈ"</string>
     <string name="instant_apps_message" msgid="6112428971833011754">"ਸਥਾਪਤ ਕੀਤੇ ਬਿਨਾਂ ਐਪ ਖੋਲ੍ਹੀ ਗਈ।"</string>
     <string name="instant_apps_message_with_help" msgid="1816952263531203932">"ਸਥਾਪਤ ਕੀਤੇ ਬਿਨਾਂ ਐਪ ਖੋਲ੍ਹੀ ਗਈ। ਹੋਰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 3524f9d..aa55dd2 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -951,7 +951,7 @@
     <string name="notification_channel_general" msgid="4384774889645929705">"பொதுச் செய்திகள்"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"சேமிப்பிடம்"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"குறிப்புகள்"</string>
-    <string name="instant_apps" msgid="8337185853050247304">"இன்ஸ்டண்ட் ஆப்ஸ்"</string>
+    <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
     <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> இயங்குகிறது"</string>
     <string name="instant_apps_message" msgid="6112428971833011754">"நிறுவ வேண்டிய தேவையில்லாமல் ஆப்ஸ் திறக்கப்பட்டது."</string>
     <string name="instant_apps_message_with_help" msgid="1816952263531203932">"நிறுவ வேண்டிய தேவையில்லாமல் ஆப்ஸ் திறக்கப்பட்டது. மேலும் அறியத் தட்டவும்."</string>
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4bba0d8..7585d6b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -1,4 +1,11 @@
 filegroup {
+    name: "services.core-sources-deviceconfig-interface",
+    srcs: [
+         "java/com/android/server/utils/DeviceConfigInterface.java"
+    ],
+}
+
+filegroup {
     name: "services.core-sources",
     srcs: ["java/**/*.java"],
     path: "java",
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 3c05080..02ee059 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -46,8 +46,10 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.display.utils.AmbientFilter;
 import com.android.server.display.utils.AmbientFilterFactory;
+import com.android.server.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -64,9 +66,11 @@
     private static final boolean DEBUG = false;
 
     private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1;
-    private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
+    private static final int MSG_LOW_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;
+    private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
+    private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5;
+    private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6;
 
     // Special ID used to indicate that given vote is to be applied globally, rather than to a
     // specific display.
@@ -79,6 +83,13 @@
     private final Context mContext;
 
     private final DisplayModeDirectorHandler mHandler;
+    private final Injector mInjector;
+
+    private final AppRequestObserver mAppRequestObserver;
+    private final SettingsObserver mSettingsObserver;
+    private final DisplayObserver mDisplayObserver;
+    private final DeviceConfigInterface mDeviceConfig;
+    private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
 
     // A map from the display ID to the collection of votes and their priority. The latter takes
     // the form of another map from the priority to the vote itself so that each priority is
@@ -89,17 +100,19 @@
     // A map from the display ID to the default mode of that display.
     private SparseArray<Display.Mode> mDefaultModeByDisplay;
 
-    private final AppRequestObserver mAppRequestObserver;
-    private final SettingsObserver mSettingsObserver;
-    private final DisplayObserver mDisplayObserver;
     private BrightnessObserver mBrightnessObserver;
 
-    private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
     private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
 
     public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
+        this(context, handler, new RealInjector());
+    }
+
+    public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
+            @NonNull Injector injector) {
         mContext = context;
         mHandler = new DisplayModeDirectorHandler(handler.getLooper());
+        mInjector = injector;
         mVotesByDisplay = new SparseArray<>();
         mSupportedModesByDisplay = new SparseArray<>();
         mDefaultModeByDisplay =  new SparseArray<>();
@@ -108,6 +121,7 @@
         mDisplayObserver = new DisplayObserver(context, handler);
         mBrightnessObserver = new BrightnessObserver(context, handler);
         mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
+        mDeviceConfig = injector.getDeviceConfig();
     }
 
     /**
@@ -349,6 +363,23 @@
     }
 
     /**
+     * Retrieve the Vote for the given display and priority. Intended only for testing purposes.
+     *
+     * @param displayId the display to query for
+     * @param priority the priority of the vote to return
+     * @return the vote corresponding to the given {@code displayId} and {@code priority},
+     *         or {@code null} if there isn't one
+     */
+    @VisibleForTesting
+    @Nullable
+    Vote getVote(int displayId, int priority) {
+        synchronized (mLock) {
+            SparseArray<Vote> votes = getVotesLocked(displayId);
+            return votes.get(priority);
+        }
+    }
+
+    /**
      * Print the object's state and debug information into the given stream.
      *
      * @param pw The stream to dump information to.
@@ -466,6 +497,17 @@
     }
 
     @VisibleForTesting
+    BrightnessObserver getBrightnessObserver() {
+        return mBrightnessObserver;
+    }
+
+    @VisibleForTesting
+    SettingsObserver getSettingsObserver() {
+        return mSettingsObserver;
+    }
+
+
+    @VisibleForTesting
     DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
             float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
         synchronized (mLock) {
@@ -493,16 +535,35 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_BRIGHTNESS_THRESHOLDS_CHANGED:
+                case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED: {
+                    Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
+                    mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged(
+                            thresholds.first, thresholds.second);
+                    break;
+                }
+
+                case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED: {
+                    int refreshRateInZone = msg.arg1;
+                    mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(
+                            refreshRateInZone);
+                    break;
+                }
+
+                case MSG_HIGH_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);
-                    }
+                    mBrightnessObserver.onDeviceConfigHighBrightnessThresholdsChanged(
+                            thresholds.first, thresholds.second);
+
                     break;
+                }
+
+                case MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED: {
+                    int refreshRateInZone = msg.arg1;
+                    mBrightnessObserver.onDeviceConfigRefreshRateInHighZoneChanged(
+                            refreshRateInZone);
+                    break;
+                }
 
                 case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
                     Float defaultPeakRefreshRate = (Float) msg.obj;
@@ -510,12 +571,6 @@
                             defaultPeakRefreshRate);
                     break;
 
-                case MSG_REFRESH_RATE_IN_ZONE_CHANGED:
-                    int refreshRateInZone = msg.arg1;
-                    mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged(
-                            refreshRateInZone);
-                    break;
-
                 case MSG_REFRESH_RATE_RANGE_CHANGED:
                     DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener =
                             (DesiredDisplayModeSpecsListener) msg.obj;
@@ -685,10 +740,11 @@
         // by all other considerations. It acts to set a default frame rate for a device.
         public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0;
 
-        // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
+        // FLICKER 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 = 1;
+        // It's used to avoid refresh rate switches in certain conditions which may result in the
+        // user seeing the display flickering when the switches occur.
+        public static final int PRIORITY_FLICKER = 1;
 
         // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
         // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
@@ -761,8 +817,8 @@
             switch (priority) {
                 case PRIORITY_DEFAULT_REFRESH_RATE:
                     return "PRIORITY_DEFAULT_REFRESH_RATE";
-                case PRIORITY_LOW_BRIGHTNESS:
-                    return "PRIORITY_LOW_BRIGHTNESS";
+                case PRIORITY_FLICKER:
+                    return "PRIORITY_FLICKER";
                 case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
                     return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
                 case PRIORITY_APP_REQUEST_REFRESH_RATE:
@@ -787,7 +843,8 @@
         }
     }
 
-    private final class SettingsObserver extends ContentObserver {
+    @VisibleForTesting
+    final class SettingsObserver extends ContentObserver {
         private final Uri mPeakRefreshRateSetting =
                 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
         private final Uri mMinRefreshRateSetting =
@@ -810,8 +867,7 @@
 
         public void observe() {
             final ContentResolver cr = mContext.getContentResolver();
-            cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this,
-                    UserHandle.USER_SYSTEM);
+            mInjector.registerPeakRefreshRateObserver(cr, this);
             cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
                     UserHandle.USER_SYSTEM);
             cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
@@ -829,6 +885,13 @@
             }
         }
 
+        public void setDefaultRefreshRate(float refreshRate) {
+            synchronized (mLock) {
+                mDefaultRefreshRate = refreshRate;
+                updateRefreshRateSettingLocked();
+            }
+        }
+
         public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
             if (defaultPeakRefreshRate == null) {
                 defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
@@ -1033,6 +1096,7 @@
         @Override
         public void onDisplayChanged(int displayId) {
             updateDisplayModes(displayId);
+            // TODO: Break the coupling between DisplayObserver and BrightnessObserver.
             mBrightnessObserver.onDisplayChanged(displayId);
         }
 
@@ -1071,15 +1135,16 @@
      */
     @VisibleForTesting
     public class BrightnessObserver extends ContentObserver {
-        // TODO: brightnessfloat: change this to the float setting
-        private final Uri mDisplayBrightnessSetting =
-                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
         private final static int LIGHT_SENSOR_RATE_MS = 250;
-        private int[] mDisplayBrightnessThresholds;
-        private int[] mAmbientBrightnessThresholds;
+        private int[] mLowDisplayBrightnessThresholds;
+        private int[] mLowAmbientBrightnessThresholds;
+        private int[] mHighDisplayBrightnessThresholds;
+        private int[] mHighAmbientBrightnessThresholds;
         // valid threshold if any item from the array >= 0
-        private boolean mShouldObserveDisplayChange;
-        private boolean mShouldObserveAmbientChange;
+        private boolean mShouldObserveDisplayLowChange;
+        private boolean mShouldObserveAmbientLowChange;
+        private boolean mShouldObserveDisplayHighChange;
+        private boolean mShouldObserveAmbientHighChange;
 
         private SensorManager mSensorManager;
         private Sensor mLightSensor;
@@ -1087,46 +1152,122 @@
         // Take it as low brightness before valid sensor data comes
         private float mAmbientLux = -1.0f;
         private AmbientFilter mAmbientFilter;
+        private int mBrightness = -1;
 
         private final Context mContext;
 
-        // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak
-        // refresh rate changeable and low power mode off. After initialization, these states will
+        // Enable light sensor only when mShouldObserveAmbientLowChange is true or
+        // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
+        // changeable and low power mode off. After initialization, these states will
         // be updated from the same handler thread.
-        private boolean mScreenOn = false;
+        private boolean mDefaultDisplayOn = false;
         private boolean mRefreshRateChangeable = false;
         private boolean mLowPowerModeEnabled = false;
 
-        private int mRefreshRateInZone;
+        private int mRefreshRateInLowZone;
+        private int mRefreshRateInHighZone;
 
         BrightnessObserver(Context context, Handler handler) {
             super(handler);
             mContext = context;
-            mDisplayBrightnessThresholds = context.getResources().getIntArray(
+            mLowDisplayBrightnessThresholds = context.getResources().getIntArray(
                     R.array.config_brightnessThresholdsOfPeakRefreshRate);
-            mAmbientBrightnessThresholds = context.getResources().getIntArray(
+            mLowAmbientBrightnessThresholds = 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");
+            if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
+                throw new RuntimeException("display low brightness threshold array and ambient "
+                        + "brightness threshold array have different length: "
+                        + "displayBrightnessThresholds="
+                        + Arrays.toString(mLowDisplayBrightnessThresholds)
+                        + ", ambientBrightnessThresholds="
+                        + Arrays.toString(mLowAmbientBrightnessThresholds));
             }
+
+            mHighDisplayBrightnessThresholds = context.getResources().getIntArray(
+                    R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
+            mHighAmbientBrightnessThresholds = context.getResources().getIntArray(
+                    R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
+            if (mHighDisplayBrightnessThresholds.length
+                    != mHighAmbientBrightnessThresholds.length) {
+                throw new RuntimeException("display high brightness threshold array and ambient "
+                        + "brightness threshold array have different length: "
+                        + "displayBrightnessThresholds="
+                        + Arrays.toString(mHighDisplayBrightnessThresholds)
+                        + ", ambientBrightnessThresholds="
+                        + Arrays.toString(mHighAmbientBrightnessThresholds));
+            }
+            mRefreshRateInHighZone = context.getResources().getInteger(
+                    R.integer.config_fixedRefreshRateInHighZone);
+        }
+
+        /**
+         * @return the refresh to lock to when in a low brightness zone
+         */
+        @VisibleForTesting
+        int getRefreshRateInLowZone() {
+            return mRefreshRateInLowZone;
+        }
+
+        /**
+         * @return the display brightness thresholds for the low brightness zones
+         */
+        @VisibleForTesting
+        int[] getLowDisplayBrightnessThresholds() {
+            return mLowDisplayBrightnessThresholds;
+        }
+
+        /**
+         * @return the ambient brightness thresholds for the low brightness zones
+         */
+        @VisibleForTesting
+        int[] getLowAmbientBrightnessThresholds() {
+            return mLowAmbientBrightnessThresholds;
+        }
+
+        public void registerLightSensor(SensorManager sensorManager, Sensor lightSensor) {
+            mSensorManager = sensorManager;
+            mLightSensor = lightSensor;
+
+            mSensorManager.registerListener(mLightSensorListener,
+                    mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
         }
 
         public void observe(SensorManager sensorManager) {
             mSensorManager = sensorManager;
+            final ContentResolver cr = mContext.getContentResolver();
+            mBrightness = Settings.System.getIntForUser(cr,
+                    Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
 
             // DeviceConfig is accessible after system ready.
-            int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds();
-            int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds();
+            int[] lowDisplayBrightnessThresholds =
+                    mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds();
+            int[] lowAmbientBrightnessThresholds =
+                    mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds();
 
-            if (brightnessThresholds != null && ambientThresholds != null
-                    && brightnessThresholds.length == ambientThresholds.length) {
-                mDisplayBrightnessThresholds = brightnessThresholds;
-                mAmbientBrightnessThresholds = ambientThresholds;
+            if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null
+                    && lowDisplayBrightnessThresholds.length
+                    == lowAmbientBrightnessThresholds.length) {
+                mLowDisplayBrightnessThresholds = lowDisplayBrightnessThresholds;
+                mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds;
             }
 
-            mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone();
+
+            int[] highDisplayBrightnessThresholds =
+                    mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds();
+            int[] highAmbientBrightnessThresholds =
+                    mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds();
+
+            if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null
+                    && highDisplayBrightnessThresholds.length
+                    == highAmbientBrightnessThresholds.length) {
+                mHighDisplayBrightnessThresholds = highDisplayBrightnessThresholds;
+                mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds;
+            }
+
+            mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone();
+            mRefreshRateInHighZone = mDeviceConfigDisplaySettings.getRefreshRateInHighZone();
+
             restartObserver();
             mDeviceConfigDisplaySettings.startListening();
         }
@@ -1138,7 +1279,7 @@
                 updateSensorStatus();
                 if (!changeable) {
                     // Revoke previous vote from BrightnessObserver
-                    updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null);
+                    updateVoteLocked(Vote.PRIORITY_FLICKER, null);
                 }
             }
         }
@@ -1150,25 +1291,48 @@
             }
         }
 
-        public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds,
+        public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds,
                 int[] ambientThresholds) {
-            if (brightnessThresholds != null && ambientThresholds != null
-                    && brightnessThresholds.length == ambientThresholds.length) {
-                mDisplayBrightnessThresholds = brightnessThresholds;
-                mAmbientBrightnessThresholds = ambientThresholds;
+            if (displayThresholds != null && ambientThresholds != null
+                    && displayThresholds.length == ambientThresholds.length) {
+                mLowDisplayBrightnessThresholds = displayThresholds;
+                mLowAmbientBrightnessThresholds = ambientThresholds;
             } else {
                 // Invalid or empty. Use device default.
-                mDisplayBrightnessThresholds = mContext.getResources().getIntArray(
+                mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray(
                         R.array.config_brightnessThresholdsOfPeakRefreshRate);
-                mAmbientBrightnessThresholds = mContext.getResources().getIntArray(
+                mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray(
                         R.array.config_ambientThresholdsOfPeakRefreshRate);
             }
             restartObserver();
         }
 
-        public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) {
-            if (refreshRate != mRefreshRateInZone) {
-                mRefreshRateInZone = refreshRate;
+        public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) {
+            if (refreshRate != mRefreshRateInLowZone) {
+                mRefreshRateInLowZone = refreshRate;
+                restartObserver();
+            }
+        }
+
+        public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds,
+                int[] ambientThresholds) {
+            if (displayThresholds != null && ambientThresholds != null
+                    && displayThresholds.length == ambientThresholds.length) {
+                mHighDisplayBrightnessThresholds = displayThresholds;
+                mHighAmbientBrightnessThresholds = ambientThresholds;
+            } else {
+                // Invalid or empty. Use device default.
+                mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray(
+                        R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
+                mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray(
+                        R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
+            }
+            restartObserver();
+        }
+
+        public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) {
+            if (refreshRate != mRefreshRateInHighZone) {
+                mRefreshRateInHighZone = refreshRate;
                 restartObserver();
             }
         }
@@ -1176,48 +1340,95 @@
         public void dumpLocked(PrintWriter pw) {
             pw.println("  BrightnessObserver");
             pw.println("    mAmbientLux: " + mAmbientLux);
-            pw.println("    mRefreshRateInZone: " + mRefreshRateInZone);
+            pw.println("    mBrightness: " + mBrightness);
+            pw.println("    mDefaultDisplayOn: " + mDefaultDisplayOn);
+            pw.println("    mLowPowerModeEnabled: " + mLowPowerModeEnabled);
+            pw.println("    mRefreshRateChangeable: " + mRefreshRateChangeable);
+            pw.println("    mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
+            pw.println("    mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange);
+            pw.println("    mRefreshRateInLowZone: " + mRefreshRateInLowZone);
 
-            for (int d: mDisplayBrightnessThresholds) {
-                pw.println("    mDisplayBrightnessThreshold: " + d);
+            for (int d : mLowDisplayBrightnessThresholds) {
+                pw.println("    mDisplayLowBrightnessThreshold: " + d);
             }
 
-            for (int d: mAmbientBrightnessThresholds) {
-                pw.println("    mAmbientBrightnessThreshold: " + d);
+            for (int d : mLowAmbientBrightnessThresholds) {
+                pw.println("    mAmbientLowBrightnessThreshold: " + d);
+            }
+
+            pw.println("    mShouldObserveDisplayHighChange: " + mShouldObserveDisplayHighChange);
+            pw.println("    mShouldObserveAmbientHighChange: " + mShouldObserveAmbientHighChange);
+            pw.println("    mRefreshRateInHighZone: " + mRefreshRateInHighZone);
+
+            for (int d : mHighDisplayBrightnessThresholds) {
+                pw.println("    mDisplayHighBrightnessThresholds: " + d);
+            }
+
+            for (int d : mHighAmbientBrightnessThresholds) {
+                pw.println("    mAmbientHighBrightnessThresholds: " + d);
             }
 
             mLightSensorListener.dumpLocked(pw);
+
+            if (mAmbientFilter != null) {
+                IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+                ipw.setIndent("    ");
+                mAmbientFilter.dump(ipw);
+            }
         }
 
         public void onDisplayChanged(int displayId) {
             if (displayId == Display.DEFAULT_DISPLAY) {
-                onScreenOn(isDefaultDisplayOn());
+                updateDefaultDisplayState();
             }
         }
 
         @Override
         public void onChange(boolean selfChange, Uri uri, int userId) {
             synchronized (mLock) {
-                onBrightnessChangedLocked();
+                final ContentResolver cr = mContext.getContentResolver();
+                int brightness = Settings.System.getIntForUser(cr,
+                        Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
+                if (brightness != mBrightness) {
+                    mBrightness = brightness;
+                    onBrightnessChangedLocked();
+                }
             }
         }
 
         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);
+
+            if (mRefreshRateInLowZone > 0) {
+                mShouldObserveDisplayLowChange = hasValidThreshold(
+                        mLowDisplayBrightnessThresholds);
+                mShouldObserveAmbientLowChange = hasValidThreshold(
+                        mLowAmbientBrightnessThresholds);
             } else {
-                cr.unregisterContentObserver(this);
+                mShouldObserveDisplayLowChange = false;
+                mShouldObserveAmbientLowChange = false;
             }
 
-            if (mShouldObserveAmbientChange) {
+            if (mRefreshRateInHighZone > 0) {
+                mShouldObserveDisplayHighChange = hasValidThreshold(
+                        mHighDisplayBrightnessThresholds);
+                mShouldObserveAmbientHighChange = hasValidThreshold(
+                        mHighAmbientBrightnessThresholds);
+            } else {
+                mShouldObserveDisplayHighChange = false;
+                mShouldObserveAmbientHighChange = false;
+            }
+
+            if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) {
+                // Content Service does not check if an listener has already been registered.
+                // To ensure only one listener is registered, force an unregistration first.
+                mInjector.unregisterBrightnessObserver(cr, this);
+                mInjector.registerBrightnessObserver(cr, this);
+            } else {
+                mInjector.unregisterBrightnessObserver(cr, this);
+            }
+
+            if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
                 Resources resources = mContext.getResources();
                 String lightSensorType = resources.getString(
                         com.android.internal.R.string.config_displayLightSensorType);
@@ -1243,8 +1454,6 @@
 
                     mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
                     mLightSensor = lightSensor;
-
-                    onScreenOn(isDefaultDisplayOn());
                 }
             } else {
                 mAmbientFilter = null;
@@ -1263,11 +1472,7 @@
          * Checks to see if at least one value is positive, in which case it is necessary to listen
          * to value changes.
          */
-        private boolean checkShouldObserve(int[] a) {
-            if (mRefreshRateInZone <= 0) {
-                return false;
-            }
-
+        private boolean hasValidThreshold(int[] a) {
             for (int d: a) {
                 if (d >= 0) {
                     return true;
@@ -1277,13 +1482,13 @@
             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];
+        private boolean isInsideLowZone(int brightness, float lux) {
+            for (int i = 0; i < mLowDisplayBrightnessThresholds.length; i++) {
+                int disp = mLowDisplayBrightnessThresholds[i];
+                int ambi = mLowAmbientBrightnessThresholds[i];
 
                 if (disp >= 0 && ambi >= 0) {
-                    if (brightness <= disp && mAmbientLux <= ambi) {
+                    if (brightness <= disp && lux <= ambi) {
                         return true;
                     }
                 } else if (disp >= 0) {
@@ -1291,7 +1496,7 @@
                         return true;
                     }
                 } else if (ambi >= 0) {
-                    if (mAmbientLux <= ambi) {
+                    if (lux <= ambi) {
                         return true;
                     }
                 }
@@ -1299,27 +1504,77 @@
 
             return false;
         }
-        // TODO: brightnessfloat: make it use float not int
-        private void onBrightnessChangedLocked() {
-            int brightness = Settings.System.getInt(mContext.getContentResolver(),
-                    Settings.System.SCREEN_BRIGHTNESS, -1);
 
+        private boolean isInsideHighZone(int brightness, float lux) {
+            for (int i = 0; i < mHighDisplayBrightnessThresholds.length; i++) {
+                int disp = mHighDisplayBrightnessThresholds[i];
+                int ambi = mHighAmbientBrightnessThresholds[i];
+
+                if (disp >= 0 && ambi >= 0) {
+                    if (brightness >= disp && lux >= ambi) {
+                        return true;
+                    }
+                } else if (disp >= 0) {
+                    if (brightness >= disp) {
+                        return true;
+                    }
+                } else if (ambi >= 0) {
+                    if (lux >= ambi) {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+        private void onBrightnessChangedLocked() {
             Vote vote = null;
-            boolean insideZone = isInsideZone(brightness, mAmbientLux);
-            if (insideZone) {
-                vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone);
+
+            if (mBrightness < 0) {
+                // Either the setting isn't available or we shouldn't be observing yet anyways.
+                // Either way, just bail out since there's nothing we can do here.
+                return;
+            }
+
+            boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux);
+            if (insideLowZone) {
+                vote = Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
+            }
+
+            boolean insideHighZone = hasValidHighZone()
+                    && isInsideHighZone(mBrightness, mAmbientLux);
+            if (insideHighZone) {
+                vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
             }
 
             if (DEBUG) {
-                Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " +  mAmbientLux +
-                        ", Vote " + vote);
+                Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " +  mAmbientLux
+                        + ", Vote " + vote);
             }
-            updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
+            updateVoteLocked(Vote.PRIORITY_FLICKER, vote);
         }
 
-        private void onScreenOn(boolean on) {
-            if (mScreenOn != on) {
-                mScreenOn = on;
+        private boolean hasValidLowZone() {
+            return mRefreshRateInLowZone > 0
+                    && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange);
+        }
+
+        private boolean hasValidHighZone() {
+            return mRefreshRateInHighZone > 0
+                    && (mShouldObserveDisplayHighChange || mShouldObserveAmbientHighChange);
+        }
+
+        private void updateDefaultDisplayState() {
+            Display display = mContext.getSystemService(DisplayManager.class)
+                    .getDisplay(Display.DEFAULT_DISPLAY);
+            boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF;
+            setDefaultDisplayState(defaultDisplayOn);
+        }
+
+        @VisibleForTesting
+        public void setDefaultDisplayState(boolean on) {
+            if (mDefaultDisplayOn != on) {
+                mDefaultDisplayOn = on;
                 updateSensorStatus();
             }
         }
@@ -1329,8 +1584,8 @@
                 return;
             }
 
-            if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled
-                    && mRefreshRateChangeable) {
+            if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
+                     && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
                 mSensorManager.registerListener(mLightSensorListener,
                         mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
             } else {
@@ -1339,11 +1594,8 @@
             }
         }
 
-        private boolean isDefaultDisplayOn() {
-            final Display display = mContext.getSystemService(DisplayManager.class)
-                    .getDisplay(Display.DEFAULT_DISPLAY);
-            return display.getState() != Display.STATE_OFF
-                    && mContext.getSystemService(PowerManager.class).isInteractive();
+        private boolean isDeviceActive() {
+            return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext);
         }
 
         private final class LightSensorEventListener implements SensorEventListener {
@@ -1361,23 +1613,33 @@
                     Slog.d(TAG, "On sensor changed: " + mLastSensorData);
                 }
 
-                boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux);
-                if (zoneChanged && mLastSensorData < mAmbientLux) {
-                    // Easier to see flicker at lower brightness environment. Forget the history to
-                    // get immediate response.
-                    mAmbientFilter.clear();
+                boolean lowZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
+                        mLowAmbientBrightnessThresholds);
+                boolean highZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
+                        mHighAmbientBrightnessThresholds);
+                if ((lowZoneChanged && mLastSensorData < mAmbientLux)
+                        || (highZoneChanged && mLastSensorData > mAmbientLux)) {
+                    // Easier to see flicker at lower brightness environment or high brightness
+                    // environment. Forget the history to get immediate response.
+                    if (mAmbientFilter != null) {
+                        mAmbientFilter.clear();
+                    }
                 }
 
                 long now = SystemClock.uptimeMillis();
-                mAmbientFilter.addValue(now, mLastSensorData);
+                if (mAmbientFilter != null) {
+                    mAmbientFilter.addValue(now, mLastSensorData);
+                }
 
                 mHandler.removeCallbacks(mInjectSensorEventRunnable);
                 processSensorData(now);
 
-                if (zoneChanged && mLastSensorData > mAmbientLux) {
+                if ((lowZoneChanged && mLastSensorData > mAmbientLux)
+                        || (highZoneChanged && mLastSensorData < mAmbientLux)) {
                     // Sensor may not report new event if there is no brightness change.
                     // Need to keep querying the temporal filter for the latest estimation,
-                    // until enter in higher lux zone or is interrupted by a new sensor event.
+                    // until sensor readout and filter estimation are in the same zone or
+                    // is interrupted by a new sensor event.
                     mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
                 }
             }
@@ -1392,17 +1654,19 @@
             }
 
             private void processSensorData(long now) {
-                mAmbientLux = mAmbientFilter.getEstimate(now);
+                if (mAmbientFilter != null) {
+                    mAmbientLux = mAmbientFilter.getEstimate(now);
+                } else {
+                    mAmbientLux = mLastSensorData;
+                }
 
                 synchronized (mLock) {
                     onBrightnessChangedLocked();
                 }
             }
 
-            private boolean isDifferentZone(float lux1, float lux2) {
-                for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) {
-                    final float boundary = mAmbientBrightnessThresholds[z];
-
+            private boolean isDifferentZone(float lux1, float lux2, int[] luxThresholds) {
+                for (final float boundary : luxThresholds) {
                     // Test each boundary. See if the current value and the new value are at
                     // different sides.
                     if ((lux1 <= boundary && lux2 > boundary)
@@ -1422,7 +1686,10 @@
                     processSensorData(now);
 
                     // Inject next event if there is a possible zone change.
-                    if (isDifferentZone(mLastSensorData, mAmbientLux)) {
+                    if (isDifferentZone(mLastSensorData, mAmbientLux,
+                            mLowAmbientBrightnessThresholds)
+                            || isDifferentZone(mLastSensorData, mAmbientLux,
+                            mHighAmbientBrightnessThresholds)) {
                         mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
                     }
                 }
@@ -1435,33 +1702,75 @@
         }
 
         public void startListening() {
-            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+            mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
                     BackgroundThread.getExecutor(), this);
         }
 
         /*
          * Return null if no such property or wrong format (not comma separated integers).
          */
-        public int[] getBrightnessThresholds() {
+        public int[] getLowDisplayBrightnessThresholds() {
             return getIntArrayProperty(
                     DisplayManager.DeviceConfig.
-                            KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS);
+                            KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
         }
 
         /*
          * Return null if no such property or wrong format (not comma separated integers).
          */
-        public int[] getAmbientThresholds() {
+        public int[] getLowAmbientBrightnessThresholds() {
             return getIntArrayProperty(
                     DisplayManager.DeviceConfig.
-                            KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS);
+                            KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
+        }
+
+        public int getRefreshRateInLowZone() {
+            int defaultRefreshRateInZone = mContext.getResources().getInteger(
+                    R.integer.config_defaultRefreshRateInZone);
+
+            int refreshRate = mDeviceConfig.getInt(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
+                    defaultRefreshRateInZone);
+
+            return refreshRate;
+        }
+
+        /*
+         * Return null if no such property or wrong format (not comma separated integers).
+         */
+        public int[] getHighDisplayBrightnessThresholds() {
+            return getIntArrayProperty(
+                    DisplayManager.DeviceConfig
+                            .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
+        }
+
+        /*
+         * Return null if no such property or wrong format (not comma separated integers).
+         */
+        public int[] getHighAmbientBrightnessThresholds() {
+            return getIntArrayProperty(
+                    DisplayManager.DeviceConfig
+                            .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
+        }
+
+        public int getRefreshRateInHighZone() {
+            int defaultRefreshRateInZone = mContext.getResources().getInteger(
+                    R.integer.config_fixedRefreshRateInHighZone);
+
+            int refreshRate = mDeviceConfig.getInt(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
+                    defaultRefreshRateInZone);
+
+            return refreshRate;
         }
 
         /*
          * Return null if no such property
          */
         public Float getDefaultPeakRefreshRate() {
-            float defaultPeakRefreshRate = DeviceConfig.getFloat(
+            float defaultPeakRefreshRate = mDeviceConfig.getFloat(
                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
                     DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
 
@@ -1471,36 +1780,35 @@
             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();
+
+            int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds();
+            int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds();
+            int refreshRateInLowZone = getRefreshRateInLowZone();
+
+            mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
+                    new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
+                    .sendToTarget();
+            mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0)
+                    .sendToTarget();
+
+            int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
+            int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds();
+            int refreshRateInHighZone = getRefreshRateInHighZone();
+
+            mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
+                    new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
+                    .sendToTarget();
+            mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0)
+                    .sendToTarget();
         }
 
         private int[] getIntArrayProperty(String prop) {
-            String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
+            String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
                     null);
 
             if (strArray != null) {
@@ -1527,4 +1835,59 @@
         }
     }
 
+    interface Injector {
+        // TODO: brightnessfloat: change this to the float setting
+        Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
+        Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
+
+        @NonNull
+        DeviceConfigInterface getDeviceConfig();
+
+        void registerBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer);
+
+        void unregisterBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer);
+
+        void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer);
+
+        boolean isDeviceInteractive(@NonNull Context context);
+    }
+
+    @VisibleForTesting
+    static class RealInjector implements Injector {
+
+        @Override
+        @NonNull
+        public DeviceConfigInterface getDeviceConfig() {
+            return DeviceConfigInterface.REAL;
+        }
+
+        @Override
+        public void registerBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/,
+                    observer, UserHandle.USER_SYSTEM);
+        }
+
+        @Override
+        public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            cr.unregisterContentObserver(observer);
+        }
+
+        @Override
+        public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
+                    observer, UserHandle.USER_SYSTEM);
+        }
+
+        @Override
+        public boolean isDeviceInteractive(@NonNull Context ctx) {
+            return ctx.getSystemService(PowerManager.class).isInteractive();
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java b/services/core/java/com/android/server/utils/DeviceConfigInterface.java
similarity index 90%
rename from services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java
rename to services/core/java/com/android/server/utils/DeviceConfigInterface.java
index ab7e7f6..ff60903 100644
--- a/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java
+++ b/services/core/java/com/android/server/utils/DeviceConfigInterface.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.wm.utils;
+package com.android.server.utils;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -54,6 +54,11 @@
     boolean getBoolean(@NonNull String namespace, @NonNull String name, boolean defaultValue);
 
     /**
+     * @see DeviceConfig#getFloat
+     */
+    float getFloat(@NonNull String namespace, @NonNull String name, float defaultValue);
+
+    /**
      * @see DeviceConfig#addOnPropertiesChangedListener
      */
     void addOnPropertiesChangedListener(@NonNull String namespace, @NonNull Executor executor,
@@ -96,6 +101,12 @@
         }
 
         @Override
+        public float getFloat(@NonNull String namespace, @NonNull String name,
+                float defaultValue) {
+            return DeviceConfig.getFloat(namespace, name, defaultValue);
+        }
+
+        @Override
         public void addOnPropertiesChangedListener(String namespace, Executor executor,
                 DeviceConfig.OnPropertiesChangedListener listener) {
             DeviceConfig.addOnPropertiesChangedListener(namespace, executor, listener);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6e662a7..a70e30f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -188,6 +188,8 @@
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputWindowHandle;
+import android.view.InsetsSource;
+import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.MagnificationSpec;
 import android.view.RemoteAnimationDefinition;
@@ -1666,6 +1668,28 @@
         }
     }
 
+    void notifyInsetsChanged(Consumer<WindowState> dispatchInsetsChanged) {
+        if (mFixedRotationLaunchingApp != null) {
+            // The insets state of fixed rotation app is a rotated copy. Make sure the visibilities
+            // of insets sources are consistent with the latest state.
+            final InsetsState rotatedState =
+                    mFixedRotationLaunchingApp.getFixedRotationTransformInsetsState();
+            if (rotatedState != null) {
+                final InsetsState state = mInsetsStateController.getRawInsetsState();
+                for (int i = 0; i < InsetsState.SIZE; i++) {
+                    final InsetsSource source = state.peekSource(i);
+                    if (source != null) {
+                        rotatedState.setSourceVisible(i, source.isVisible());
+                    }
+                }
+            }
+        }
+        forAllWindows(dispatchInsetsChanged, true /* traverseTopToBottom */);
+        if (mRemoteInsetsControlTarget != null) {
+            mRemoteInsetsControlTarget.notifyInsetsChanged();
+        }
+    }
+
     /**
      * Update rotation of the display.
      *
diff --git a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
index d9cf637..09ab004 100644
--- a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
+++ b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
@@ -27,7 +27,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
-import com.android.server.wm.utils.DeviceConfigInterface;
+import com.android.server.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
 
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index fd67db1..be1d0fc 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -36,6 +36,7 @@
 import android.view.InsetsAnimationControlImpl;
 import android.view.InsetsAnimationControlRunner;
 import android.view.InsetsController;
+import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
@@ -212,11 +213,21 @@
      * @see InsetsStateController#getInsetsForDispatch
      */
     InsetsState getInsetsForDispatch(WindowState target) {
-        InsetsState originalState = mStateController.getInsetsForDispatch(target);
+        final InsetsState originalState = mStateController.getInsetsForDispatch(target);
         InsetsState state = originalState;
         for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
-            state = new InsetsState(state);
-            state.setSourceVisible(mShowingTransientTypes.get(i), false);
+            final int type = mShowingTransientTypes.get(i);
+            final InsetsSource originalSource = state.peekSource(type);
+            if (originalSource != null && originalSource.isVisible()) {
+                if (state == originalState) {
+                    // The source will be modified, create a non-deep copy to store the new one.
+                    state = new InsetsState(originalState);
+                }
+                // Replace the source with a copy in invisible state.
+                final InsetsSource source = new InsetsSource(originalSource);
+                source.setVisible(false);
+                state.addSource(source);
+            }
         }
         return state;
     }
@@ -252,11 +263,14 @@
         }
     }
 
+    /**
+     * If the caller is not {@link #updateBarControlTarget}, it should call
+     * updateBarControlTarget(mFocusedWin) after this invocation.
+     */
     private void abortTransient() {
         mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(),
                 mShowingTransientTypes.toArray());
         mShowingTransientTypes.clear();
-        updateBarControlTarget(mFocusedWin);
     }
 
     private @Nullable InsetsControlTarget getFakeControlTarget(@Nullable WindowState focused,
@@ -290,7 +304,7 @@
             // fake control to the client, so that it can re-show the bar during this scenario.
             return mDummyControlTarget;
         }
-        if (mPolicy.topAppHidesStatusBar()) {
+        if (!canBeTopFullscreenOpaqueWindow(focusedWin) && mPolicy.topAppHidesStatusBar()) {
             // Non-fullscreen focused window should not break the state that the top-fullscreen-app
             // window hides status bar.
             return mPolicy.getTopFullscreenOpaqueWindow();
@@ -298,6 +312,16 @@
         return focusedWin;
     }
 
+    private static boolean canBeTopFullscreenOpaqueWindow(@Nullable WindowState win) {
+        // The condition doesn't use WindowState#canAffectSystemUiFlags because the window may
+        // haven't drawn or committed the visibility.
+        final boolean nonAttachedAppWindow = win != null
+                && win.mAttrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
+                && win.mAttrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+        return nonAttachedAppWindow && win.mAttrs.isFullscreen() && !win.isFullyTransparent()
+                && !win.inMultiWindowMode();
+    }
+
     private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin,
             boolean forceShowsSystemBarsForWindowingMode) {
         if (mShowingTransientTypes.indexOf(ITYPE_NAVIGATION_BAR) != -1) {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 68405c4..c56457a 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -107,6 +107,10 @@
      * @return The state stripped of the necessary information.
      */
     InsetsState getInsetsForDispatch(@NonNull WindowState target) {
+        final InsetsState rotatedState = target.mToken.getFixedRotationTransformInsetsState();
+        if (rotatedState != null) {
+            return rotatedState;
+        }
         final InsetsSourceProvider provider = target.getControllableInsetProvider();
         final @InternalInsetsType int type = provider != null
                 ? provider.getSource().getType() : ITYPE_INVALID;
@@ -473,10 +477,7 @@
     }
 
     void notifyInsetsChanged() {
-        mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
-        if (mDisplayContent.mRemoteInsetsControlTarget != null) {
-            mDisplayContent.mRemoteInsetsControlTarget.notifyInsetsChanged();
-        }
+        mDisplayContent.notifyInsetsChanged(mDispatchInsetsChanged);
     }
 
     void dump(String prefix, PrintWriter pw) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index e85049c..3c0cb17 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -480,9 +480,7 @@
         final int color = ColorUtils.setAlphaComponent(
                 task.getTaskDescription().getBackgroundColor(), 255);
         final LayoutParams attrs = mainWindow.getAttrs();
-        final InsetsPolicy insetsPolicy = mainWindow.getDisplayContent().getInsetsPolicy();
-        final InsetsState insetsState =
-                new InsetsState(insetsPolicy.getInsetsForDispatch(mainWindow));
+        final InsetsState insetsState = new InsetsState(mainWindow.getInsetsState());
         mergeInsetsSources(insetsState, mainWindow.getRequestedInsetsState());
         final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrameLw(), insetsState);
         final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 6cc0ba5..94229b9 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -245,11 +245,7 @@
             task.getBounds(taskBounds);
             currentOrientation = topFullscreenOpaqueWindow.getConfiguration().orientation;
             activityType = activity.getActivityType();
-
-            final InsetsPolicy insetsPolicy = topFullscreenOpaqueWindow.getDisplayContent()
-                    .getInsetsPolicy();
-            insetsState =
-                    new InsetsState(insetsPolicy.getInsetsForDispatch(topFullscreenOpaqueWindow));
+            insetsState = new InsetsState(topFullscreenOpaqueWindow.getInsetsState());
             mergeInsetsSources(insetsState, topFullscreenOpaqueWindow.getRequestedInsetsState());
         }
         try {
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index b0c5dbc..a5ebf9a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -23,7 +23,7 @@
 import android.provider.DeviceConfig;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.utils.DeviceConfigInterface;
+import com.android.server.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
 import java.util.Objects;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b7a2eb3..d9594a4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -282,8 +282,8 @@
 import com.android.server.power.ShutdownThread;
 import com.android.server.protolog.ProtoLogImpl;
 import com.android.server.protolog.common.ProtoLog;
+import com.android.server.utils.DeviceConfigInterface;
 import com.android.server.utils.PriorityDump;
-import com.android.server.wm.utils.DeviceConfigInterface;
 
 import java.io.BufferedWriter;
 import java.io.DataInputStream;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3b8928e..bab83dc 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1525,11 +1525,11 @@
         return getDisplayContent().getDisplayInfo();
     }
 
+    /**
+     * Returns the insets state for the client. Its sources may be the copies with visibility
+     * modification according to the state of transient bars.
+     */
     InsetsState getInsetsState() {
-        final InsetsState insetsState = mToken.getFixedRotationTransformInsetsState();
-        if (insetsState != null) {
-            return insetsState;
-        }
         return getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this);
     }
 
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 979f4e1..e57097e 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -48,7 +48,6 @@
         // TODO: remove once Android migrates to JUnit 4.12,
         // which provides assertThrows
         "testng",
-
     ],
 
     aidl: {
@@ -110,6 +109,7 @@
         "utils/**/*.java",
         "utils/**/*.kt",
         "utils-mockito/**/*.kt",
+        ":services.core-sources-deviceconfig-interface",
     ],
     static_libs: [
         "junit",
@@ -126,6 +126,7 @@
         "utils/**/*.java",
         "utils/**/*.kt",
         "utils-mockito/**/*.kt",
+        ":services.core-sources-deviceconfig-interface",
     ],
     static_libs: [
         "junit",
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 43a396d..c467ee9 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -16,49 +16,99 @@
 
 package com.android.server.display;
 
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.verify;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
 
+import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICKER;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.ContextWrapper;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.Looper;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.display.DisplayModeDirector.BrightnessObserver;
 import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
 import com.android.server.display.DisplayModeDirector.Vote;
+import com.android.server.testutils.FakeDeviceConfigInterface;
 
 import com.google.common.truth.Truth;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DisplayModeDirectorTest {
     // The tolerance within which we consider something approximately equals.
+    private static final String TAG = "DisplayModeDirectorTest";
+    private static final boolean DEBUG = false;
     private static final float FLOAT_TOLERANCE = 0.01f;
 
     private Context mContext;
+    private FakesInjector mInjector;
+    private Handler mHandler;
+    @Rule
+    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+        final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
+        when(mContext.getContentResolver()).thenReturn(resolver);
+        mInjector = new FakesInjector();
+        mHandler = new Handler(Looper.getMainLooper());
     }
 
     private DisplayModeDirector createDirectorFromRefreshRateArray(
             float[] refreshRates, int baseModeId) {
         DisplayModeDirector director =
-                new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper()));
+                new DisplayModeDirector(mContext, mHandler, mInjector);
         int displayId = 0;
         Display.Mode[] modes = new Display.Mode[refreshRates.length];
         for (int i = 0; i < refreshRates.length; i++) {
@@ -159,9 +209,9 @@
     }
 
     @Test
-    public void testBrightnessHasLowerPriorityThanUser() {
-        assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
-        assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE);
+    public void testFlickerHasLowerPriorityThanUser() {
+        assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
+        assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_SIZE);
 
         int displayId = 0;
         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
@@ -169,7 +219,7 @@
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
         votesByDisplay.put(displayId, votes);
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
         director.injectVotesByDisplay(votesByDisplay);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -177,7 +227,7 @@
 
         votes.clear();
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
@@ -185,7 +235,7 @@
 
         votes.clear();
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
@@ -193,7 +243,7 @@
 
         votes.clear();
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60));
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -202,10 +252,10 @@
 
     @Test
     public void testAppRequestRefreshRateRange() {
-        // Confirm that the app request range doesn't include low brightness or min refresh rate
-        // settings, but does include everything else.
+        // Confirm that the app request range doesn't include flicker or min refresh rate settings,
+        // but does include everything else.
         assertTrue(
-                Vote.PRIORITY_LOW_BRIGHTNESS < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
+                PRIORITY_FLICKER < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
         assertTrue(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE
                 < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
         assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE
@@ -216,7 +266,7 @@
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
         votesByDisplay.put(displayId, votes);
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
         director.injectVotesByDisplay(votesByDisplay);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -302,4 +352,343 @@
         verifyBrightnessObserverCall(director, 90, 90, 0, 90, 90);
         verifyBrightnessObserverCall(director, 120, 90, 0, 120, 90);
     }
+
+    @Test
+    public void testBrightnessObserverGetsUpdatedRefreshRatesForZone() {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
+        SensorManager sensorManager = createMockSensorManager(createLightSensor());
+
+        final int initialRefreshRate = 60;
+        mInjector.getDeviceConfig().setRefreshRateInLowZone(initialRefreshRate);
+        director.start(sensorManager);
+        assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
+                .isEqualTo(initialRefreshRate);
+
+        final int updatedRefreshRate = 90;
+        mInjector.getDeviceConfig().setRefreshRateInLowZone(updatedRefreshRate);
+        // Need to wait for the property change to propagate to the main thread.
+        waitForIdleSync();
+        assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
+                .isEqualTo(updatedRefreshRate);
+    }
+
+    @Test
+    public void testBrightnessObserverThresholdsInZone() {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
+        SensorManager sensorManager = createMockSensorManager(createLightSensor());
+
+        final int[] initialDisplayThresholds = { 10 };
+        final int[] initialAmbientThresholds = { 20 };
+
+        final FakeDeviceConfig config = mInjector.getDeviceConfig();
+        config.setLowDisplayBrightnessThresholds(initialDisplayThresholds);
+        config.setLowAmbientBrightnessThresholds(initialAmbientThresholds);
+        director.start(sensorManager);
+
+        assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
+                .isEqualTo(initialDisplayThresholds);
+        assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
+                .isEqualTo(initialAmbientThresholds);
+
+        final int[] updatedDisplayThresholds = { 9, 14 };
+        final int[] updatedAmbientThresholds = { -1, 19 };
+        config.setLowDisplayBrightnessThresholds(updatedDisplayThresholds);
+        config.setLowAmbientBrightnessThresholds(updatedAmbientThresholds);
+        // Need to wait for the property change to propagate to the main thread.
+        waitForIdleSync();
+        assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
+                .isEqualTo(updatedDisplayThresholds);
+        assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
+                .isEqualTo(updatedAmbientThresholds);
+    }
+
+    @Test
+    public void testLockFpsForLowZone() throws Exception {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+        setPeakRefreshRate(90);
+        director.getSettingsObserver().setDefaultRefreshRate(90);
+        director.getBrightnessObserver().setDefaultDisplayState(true);
+
+        final FakeDeviceConfig config = mInjector.getDeviceConfig();
+        config.setRefreshRateInLowZone(90);
+        config.setLowDisplayBrightnessThresholds(new int[] { 10 });
+        config.setLowAmbientBrightnessThresholds(new int[] { 20 });
+
+        Sensor lightSensor = createLightSensor();
+        SensorManager sensorManager = createMockSensorManager(lightSensor);
+
+        director.start(sensorManager);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+                .registerListener(
+                        listenerCaptor.capture(),
+                        eq(lightSensor),
+                        anyInt(),
+                        any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        setBrightness(10);
+        // Sensor reads 20 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
+
+        Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+        assertVoteForRefreshRateLocked(vote, 90 /*fps*/);
+
+        setBrightness(125);
+        // Sensor reads 1000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));
+
+        vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+        assertThat(vote).isNull();
+    }
+
+    @Test
+    public void testLockFpsForHighZone() throws Exception {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+        setPeakRefreshRate(90 /*fps*/);
+        director.getSettingsObserver().setDefaultRefreshRate(90);
+        director.getBrightnessObserver().setDefaultDisplayState(true);
+
+        final FakeDeviceConfig config = mInjector.getDeviceConfig();
+        config.setRefreshRateInHighZone(60);
+        config.setHighDisplayBrightnessThresholds(new int[] { 255 });
+        config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+
+        Sensor lightSensor = createLightSensor();
+        SensorManager sensorManager = createMockSensorManager(lightSensor);
+
+        director.start(sensorManager);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+                .registerListener(
+                        listenerCaptor.capture(),
+                        eq(lightSensor),
+                        anyInt(),
+                        any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        setBrightness(100);
+        // Sensor reads 2000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
+
+        Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+        assertThat(vote).isNull();
+
+        setBrightness(255);
+        // Sensor reads 9000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
+
+        vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+        assertVoteForRefreshRateLocked(vote, 60 /*fps*/);
+    }
+
+    private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) {
+        assertThat(vote).isNotNull();
+        final DisplayModeDirector.RefreshRateRange expectedRange =
+                new DisplayModeDirector.RefreshRateRange(refreshRate, refreshRate);
+        assertThat(vote.refreshRateRange).isEqualTo(expectedRange);
+    }
+
+    private static class FakeDeviceConfig extends FakeDeviceConfigInterface {
+        @Override
+        public String getProperty(String namespace, String name) {
+            Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
+            return super.getProperty(namespace, name);
+        }
+
+        @Override
+        public void addOnPropertiesChangedListener(
+                String namespace,
+                Executor executor,
+                DeviceConfig.OnPropertiesChangedListener listener) {
+            Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
+            super.addOnPropertiesChangedListener(namespace, executor, listener);
+        }
+
+        void setRefreshRateInLowZone(int fps) {
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_LOW_ZONE,
+                    String.valueOf(fps));
+        }
+
+        void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) {
+            String thresholds = toPropertyValue(brightnessThresholds);
+
+            if (DEBUG) {
+                Slog.e(TAG, "Brightness Thresholds = " + thresholds);
+            }
+
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS,
+                    thresholds);
+        }
+
+        void setLowAmbientBrightnessThresholds(int[] ambientThresholds) {
+            String thresholds = toPropertyValue(ambientThresholds);
+
+            if (DEBUG) {
+                Slog.e(TAG, "Ambient Thresholds = " + thresholds);
+            }
+
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS,
+                    thresholds);
+        }
+
+        void setRefreshRateInHighZone(int fps) {
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_HIGH_ZONE,
+                    String.valueOf(fps));
+        }
+
+        void setHighDisplayBrightnessThresholds(int[] brightnessThresholds) {
+            String thresholds = toPropertyValue(brightnessThresholds);
+
+            if (DEBUG) {
+                Slog.e(TAG, "Brightness Thresholds = " + thresholds);
+            }
+
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS,
+                    thresholds);
+        }
+
+        void setHighAmbientBrightnessThresholds(int[] ambientThresholds) {
+            String thresholds = toPropertyValue(ambientThresholds);
+
+            if (DEBUG) {
+                Slog.e(TAG, "Ambient Thresholds = " + thresholds);
+            }
+
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS,
+                    thresholds);
+        }
+
+        @NonNull
+        private static String toPropertyValue(@NonNull int[] intArray) {
+            return Arrays.stream(intArray)
+                    .mapToObj(Integer::toString)
+                    .collect(Collectors.joining(","));
+        }
+    }
+
+    private void setBrightness(int brightness) {
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS,
+                brightness);
+        mInjector.notifyBrightnessChanged();
+        waitForIdleSync();
+    }
+
+    private void setPeakRefreshRate(float fps) {
+        Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE,
+                 fps);
+        mInjector.notifyPeakRefreshRateChanged();
+        waitForIdleSync();
+    }
+
+    private static SensorManager createMockSensorManager(Sensor... sensors) {
+        SensorManager sensorManager = Mockito.mock(SensorManager.class);
+        when(sensorManager.getSensorList(anyInt())).then((invocation) -> {
+            List<Sensor> requestedSensors = new ArrayList<>();
+            int type = invocation.getArgument(0);
+            for (Sensor sensor : sensors) {
+                if (sensor.getType() == type || type == Sensor.TYPE_ALL) {
+                    requestedSensors.add(sensor);
+                }
+            }
+            return requestedSensors;
+        });
+
+        when(sensorManager.getDefaultSensor(anyInt())).then((invocation) -> {
+            int type = invocation.getArgument(0);
+            for (Sensor sensor : sensors) {
+                if (sensor.getType() == type) {
+                    return sensor;
+                }
+            }
+            return null;
+        });
+        return sensorManager;
+    }
+
+    private static Sensor createLightSensor() {
+        try {
+            return TestUtils.createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
+        } catch (Exception e) {
+            // There's nothing we can do if this fails, just throw a RuntimeException so that we
+            // don't have to mark every function that might call this as throwing Exception
+            throw new RuntimeException("Failed to create a light sensor", e);
+        }
+    }
+
+    private void waitForIdleSync() {
+        mHandler.runWithScissors(() -> { }, 500 /*timeout*/);
+    }
+
+    static class FakesInjector implements DisplayModeDirector.Injector {
+        private final FakeDeviceConfig mDeviceConfig;
+        private ContentObserver mBrightnessObserver;
+        private ContentObserver mPeakRefreshRateObserver;
+
+        FakesInjector() {
+            mDeviceConfig = new FakeDeviceConfig();
+        }
+
+        @NonNull
+        public FakeDeviceConfig getDeviceConfig() {
+            return mDeviceConfig;
+        }
+
+        @Override
+        public void registerBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            if (mBrightnessObserver != null) {
+                throw new IllegalStateException("Tried to register a second brightness observer");
+            }
+            mBrightnessObserver = observer;
+        }
+
+        @Override
+        public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            mBrightnessObserver = null;
+        }
+
+        void notifyBrightnessChanged() {
+            if (mBrightnessObserver != null) {
+                mBrightnessObserver.dispatchChange(false /*selfChange*/, DISPLAY_BRIGHTNESS_URI);
+            }
+        }
+
+        @Override
+        public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            mPeakRefreshRateObserver = observer;
+        }
+
+        void notifyPeakRefreshRateChanged() {
+            if (mPeakRefreshRateObserver != null) {
+                mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
+                        PEAK_REFRESH_RATE_URI);
+            }
+        }
+
+        @Override
+        public boolean isDeviceInteractive(@NonNull Context context) {
+            return true;
+        }
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
similarity index 92%
rename from services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java
rename to services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
index 2904a5b..a67f645 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.wm.utils;
+package com.android.server.testutils;
 
 import android.annotation.NonNull;
 import android.provider.DeviceConfig;
@@ -22,6 +22,7 @@
 import android.util.Pair;
 
 import com.android.internal.util.Preconditions;
+import com.android.server.utils.DeviceConfigInterface;
 
 import java.lang.reflect.Constructor;
 import java.util.HashMap;
@@ -122,6 +123,19 @@
     }
 
     @Override
+    public float getFloat(String namespace, String name, float defaultValue) {
+        String value = getProperty(namespace, name);
+        if (value == null) {
+            return defaultValue;
+        }
+        try {
+            return Float.parseFloat(value);
+        } catch (NumberFormatException e) {
+            return defaultValue;
+        }
+    }
+
+    @Override
     public boolean getBoolean(String namespace, String name, boolean defaultValue) {
         String value = getProperty(namespace, name);
         return value != null ? Boolean.parseBoolean(value) : defaultValue;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 1948003a..d99606b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -32,6 +32,7 @@
 import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.fromBoundingRect;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_90;
 import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
@@ -102,6 +103,7 @@
 import android.view.IDisplayWindowRotationController;
 import android.view.ISystemGestureExclusionListener;
 import android.view.IWindowManager;
+import android.view.InsetsState;
 import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.SurfaceControl.Transaction;
@@ -1114,6 +1116,17 @@
         assertTrue(mNavBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS,
                 ANIMATION_TYPE_FIXED_TRANSFORM));
 
+        // If the visibility of insets state is changed, the rotated state should be updated too.
+        final InsetsState rotatedState = app.getFixedRotationTransformInsetsState();
+        final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
+        assertEquals(state.getSource(ITYPE_STATUS_BAR).isVisible(),
+                rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
+        state.getSource(ITYPE_STATUS_BAR).setVisible(
+                !rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
+        mDisplayContent.getInsetsStateController().notifyInsetsChanged();
+        assertEquals(state.getSource(ITYPE_STATUS_BAR).isVisible(),
+                rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
+
         final Rect outFrame = new Rect();
         final Rect outInsets = new Rect();
         final Rect outStableInsets = new Rect();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 58d4104..7e36134 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -52,10 +52,13 @@
 import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT;
 import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.spy;
 import static org.testng.Assert.expectThrows;
@@ -927,6 +930,25 @@
     }
 
     @Test
+    public void testFixedRotationInsetsSourceFrame() {
+        final DisplayInfo info = mDisplayContent.getDisplayInfo();
+        info.rotation = mFrames.mRotation;
+        mDisplayContent.mBaseDisplayHeight = info.logicalHeight = mFrames.mDisplayHeight;
+        mDisplayContent.mBaseDisplayWidth = info.logicalWidth = mFrames.mDisplayWidth;
+        mDisplayContent.getInsetsStateController().onPostLayout();
+        mDisplayPolicy.beginLayoutLw(mFrames, mDisplayContent.getConfiguration().uiMode);
+        doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent)
+                .rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord));
+        final Rect frame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
+        doReturn(mDisplayPolicy).when(mDisplayContent).getDisplayPolicy();
+        mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord);
+        final Rect rotatedFrame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
+
+        assertEquals(DISPLAY_WIDTH, frame.width());
+        assertEquals(DISPLAY_HEIGHT, rotatedFrame.width());
+    }
+
+    @Test
     public void testScreenDecorWindows() {
         final WindowState decorWindow = createWindow(null, TYPE_APPLICATION_OVERLAY, "decorWindow");
         mWindow.mAttrs.flags = FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR
diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
index 56cb447..a85e1db 100644
--- a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
@@ -31,7 +31,7 @@
 
 import com.android.internal.R;
 import com.android.internal.util.Preconditions;
-import com.android.server.wm.utils.FakeDeviceConfigInterface;
+import com.android.server.testutils.FakeDeviceConfigInterface;
 
 import org.junit.After;
 import org.junit.Test;
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 90af8a1..7d84920 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -38,10 +38,13 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
+import android.app.StatusBarManager;
 import android.platform.test.annotations.Presubmit;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
@@ -49,6 +52,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.statusbar.StatusBarManagerInternal;
+
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -222,6 +227,23 @@
         assertNotNull(fullscreenAppControls);
         assertEquals(1, fullscreenAppControls.length);
         assertEquals(ITYPE_STATUS_BAR, fullscreenAppControls[0].getType());
+
+        // Assume mFocusedWindow is updated but mTopFullscreenOpaqueWindowState hasn't.
+        final WindowState newFocusedFullscreenApp = addWindow(TYPE_APPLICATION, "newFullscreenApp");
+        final InsetsState newRequestedState = new InsetsState();
+        newRequestedState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+        newFocusedFullscreenApp.updateRequestedInsetsState(newRequestedState);
+        // Make sure status bar is hidden by previous insets state.
+        mDisplayContent.getInsetsPolicy().updateBarControlTarget(fullscreenApp);
+
+        final StatusBarManagerInternal sbmi =
+                mDisplayContent.getDisplayPolicy().getStatusBarManagerInternal();
+        clearInvocations(sbmi);
+        mDisplayContent.getInsetsPolicy().updateBarControlTarget(newFocusedFullscreenApp);
+        // The status bar should be shown by newFocusedFullscreenApp even
+        // mTopFullscreenOpaqueWindowState is still fullscreenApp.
+        verify(sbmi).setWindowState(mDisplayContent.mDisplayId, StatusBarManager.WINDOW_STATUS_BAR,
+                StatusBarManager.WINDOW_STATE_SHOWING);
     }
 
     @Test
@@ -309,6 +331,15 @@
         final InsetsState state = policy.getInsetsForDispatch(mAppWindow);
         state.setSourceVisible(ITYPE_STATUS_BAR, true);
         state.setSourceVisible(ITYPE_NAVIGATION_BAR, true);
+
+        final InsetsState clientState = mAppWindow.getInsetsState();
+        // The transient bar states for client should be invisible.
+        assertFalse(clientState.getSource(ITYPE_STATUS_BAR).isVisible());
+        assertFalse(clientState.getSource(ITYPE_NAVIGATION_BAR).isVisible());
+        // The original state shouldn't be modified.
+        assertTrue(state.getSource(ITYPE_STATUS_BAR).isVisible());
+        assertTrue(state.getSource(ITYPE_NAVIGATION_BAR).isVisible());
+
         policy.onInsetsModified(mAppWindow, state);
         waitUntilWindowAnimatorIdle();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 73ac408..3db0018 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -30,6 +30,9 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -329,6 +332,26 @@
         assertNull(getController().getControlsForDispatch(app));
     }
 
+    @Test
+    public void testTransientVisibilityOfFixedRotationState() {
+        final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final InsetsSourceProvider provider = getController().getSourceProvider(ITYPE_STATUS_BAR);
+        provider.setWindow(statusBar, null, null);
+
+        final InsetsState rotatedState = new InsetsState(app.getInsetsState(),
+                true /* copySources */);
+        spyOn(app.mToken);
+        doReturn(rotatedState).when(app.mToken).getFixedRotationTransformInsetsState();
+        assertTrue(rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
+
+        provider.getSource().setVisible(false);
+        mDisplayContent.getInsetsPolicy().showTransient(new int[] { ITYPE_STATUS_BAR });
+
+        assertTrue(mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR));
+        assertFalse(app.getInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
+    }
+
     private InsetsStateController getController() {
         return mDisplayContent.getInsetsStateController();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index a979c86..250dc24 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -568,6 +568,8 @@
     private static WindowState addWindowToActivity(ActivityRecord activity) {
         final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
         params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+        params.setFitInsetsSides(0);
+        params.setFitInsetsTypes(0);
         final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
                 activity.mWmService, mock(Session.class), new TestIWindow(), params, activity);
         WindowTestsBase.makeWindowVisible(w);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
index 5210011..7a0ef0d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
@@ -32,7 +32,7 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.server.wm.utils.FakeDeviceConfigInterface;
+import com.android.server.testutils.FakeDeviceConfigInterface;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/FakeSettingsProvider.java b/tests/utils/testutils/java/com/android/internal/util/test/FakeSettingsProvider.java
index e482708..a0a5a29 100644
--- a/tests/utils/testutils/java/com/android/internal/util/test/FakeSettingsProvider.java
+++ b/tests/utils/testutils/java/com/android/internal/util/test/FakeSettingsProvider.java
@@ -92,6 +92,14 @@
     }
 
     /**
+     * Creates a {@link org.junit.rules.TestRule} that makes sure {@link #clearSettingsProvider()}
+     * is triggered before and after each test.
+     */
+    public static FakeSettingsProviderRule rule() {
+        return new FakeSettingsProviderRule();
+    }
+
+    /**
      * This needs to be called before and after using the FakeSettingsProvider class.
      */
     public static void clearSettingsProvider() {
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/FakeSettingsProviderRule.java b/tests/utils/testutils/java/com/android/internal/util/test/FakeSettingsProviderRule.java
new file mode 100644
index 0000000..c4e9545
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/internal/util/test/FakeSettingsProviderRule.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package com.android.internal.util.test;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * JUnit Rule helps keeping test {@link FakeSettingsProvider} clean.
+ *
+ * <p>It clears {@link FakeSettingsProvider} before and after each test. Example use:
+ * <pre class="code"><code class="java">
+ * public class ExampleTest {
+ *
+ *     &#064;Rule public FakeSettingsProviderRule rule = FakeSettingsProvider.rule();
+ *
+ *     &#064;Test
+ *     public void shouldDoSomething() {
+ *         ContextResolver cr = rule.mockContentResolver(mContext);
+ *         Settings.Global.putInt(cr, "my_setting_name", 1);
+ *         // Test code relying on my_setting_name value using cr
+ *     }
+ * }
+ * </code></pre>
+ *
+ * @see FakeSettingsProvider
+ */
+public final class FakeSettingsProviderRule implements TestRule {
+
+    /** Prevent initialization outside {@link FakeSettingsProvider}. */
+    FakeSettingsProviderRule() {
+    }
+
+    /**
+     * Creates a {@link MockContentResolver} that uses the {@link FakeSettingsProvider} as the
+     * {@link Settings#AUTHORITY} provider.
+     */
+    public MockContentResolver mockContentResolver(Context context) {
+        MockContentResolver contentResolver = new MockContentResolver(context);
+        contentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        return contentResolver;
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            public void evaluate() throws Throwable {
+                FakeSettingsProvider.clearSettingsProvider();
+                try {
+                    base.evaluate();
+                } finally {
+                    FakeSettingsProvider.clearSettingsProvider();
+                }
+            }
+        };
+    }
+}