Merge "Add separate user consent for Platform VPNs"
diff --git a/Android.bp b/Android.bp
index c899179..f48e6fe 100644
--- a/Android.bp
+++ b/Android.bp
@@ -401,7 +401,7 @@
         "app-compat-annotations",
         "ext",
         "unsupportedappusage",
-        "updatable_media_stubs",
+        "framework-media-stubs-systemapi",
         "framework_mediaprovider_stubs",
         "framework-tethering",
         "framework-telephony-stubs",
@@ -469,7 +469,7 @@
         "framework-appsearch-stubs",
         "framework-sdkextensions-stubs-systemapi",
         "framework-statsd", // TODO(b/146167933): Use framework-statsd-stubs
-        "framework-permission-stubs",
+        "framework-permission-stubs-systemapi",
         "framework-wifi-stubs",
         "ike-stubs",
     ],
@@ -517,10 +517,10 @@
     installable: false, // this lib is a build-only library
     static_libs: [
         "framework-minus-apex",
-        "updatable_media_stubs",
+        "framework-media-stubs-systemapi",
         "framework_mediaprovider_stubs",
         "framework-appsearch", // TODO(b/146218515): should be framework-appsearch-stubs
-        "framework-permission-stubs",
+        "framework-permission-stubs-systemapi",
         "framework-sdkextensions-stubs-systemapi",
         // TODO(b/146167933): Use framework-statsd-stubs instead.
         "framework-statsd",
diff --git a/ApiDocs.bp b/ApiDocs.bp
index c40004c..04ddc50 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -176,6 +176,7 @@
     hdf: [
         "android.whichdoc offline",
     ],
+    compat_config: ":global-compat-config",
     proofread_file: "offline-sdk-docs-proofrerad.txt",
     args: framework_docs_only_args + " -offlinemode -title \"Android SDK\"",
     static_doc_index_redirect: "docs/docs-preview-index.html",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 0f80565..d4db737 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -113,6 +113,16 @@
     jdiff_enabled: true,
 }
 
+priv_apps = " " +
+    "--show-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
+    "\\) "
+
+module_libs = " " +
+    " --show-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
+    "\\) "
+
 droidstubs {
     name: "system-api-stubs-docs",
     defaults: ["metalava-api-stubs-default"],
@@ -124,10 +134,7 @@
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
-    args: metalava_framework_docs_args +
-        " --show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
-        "process=android.annotation.SystemApi.Process.ALL\\)",
+    args: metalava_framework_docs_args + priv_apps,
     check_api: {
         current: {
             api_file: "api/system-current.txt",
@@ -175,49 +182,17 @@
 // @SystemApi(client=MODULE_APPS) and @SystemApi(client=MODULE_LIBRARIES)
 /////////////////////////////////////////////////////////////////////
 
-// TODO(b/146727827) remove the *-api modules when we can teach metalava
+// TODO(b/146727827) remove the *-api module when we can teach metalava
 // about the relationship among the API surfaces. Currently, these modules are only to generate
 // the API signature files and ensure that the APIs evolve in a backwards compatible manner.
 // They however are NOT used for building the API stub.
-droidstubs {
-    name: "module-app-api",
-    defaults: ["metalava-api-stubs-default"],
-    libs: ["framework-all"],
-    arg_files: ["core/res/AndroidManifest.xml"],
-    args: metalava_framework_docs_args +
-        " --show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.MODULE_APPS," +
-        "process=android.annotation.SystemApi.Process.ALL\\)",
-    check_api: {
-        current: {
-            api_file: "api/module-app-current.txt",
-            removed_api_file: "api/module-app-removed.txt",
-        },
-        // TODO(b/147559833) enable the compatibility check against the last release API
-        // and the API lint
-        //last_released: {
-        //    api_file: ":last-released-module-app-api",
-        //    removed_api_file: "api/module-app-removed.txt",
-        //    baseline_file: ":module-app-api-incompatibilities-with-last-released"
-        //},
-        //api_lint: {
-        //    enabled: true,
-        //    new_since: ":last-released-module-app-api",
-        //    baseline_file: "api/module-app-lint-baseline.txt",
-        //},
-    },
-    //jdiff_enabled: true,
-}
 
 droidstubs {
     name: "module-lib-api",
     defaults: ["metalava-api-stubs-default"],
     libs: ["framework-all"],
     arg_files: ["core/res/AndroidManifest.xml"],
-    args: metalava_framework_docs_args +
-        " --show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
-        "process=android.annotation.SystemApi.Process.ALL\\)",
+    args: metalava_framework_docs_args + module_libs,
     check_api: {
         current: {
             api_file: "api/module-lib-current.txt",
@@ -239,39 +214,17 @@
     //jdiff_enabled: true,
 }
 
-// The following two droidstubs modules generate source files for the API stub libraries for
-// modules. Note that they not only include their own APIs but also other APIs that have
-// narrower scope. For example, module-lib-api-stubs-docs includes all @SystemApis not just
-// the ones with 'client=MODULE_LIBRARIES'.
-droidstubs {
-    name: "module-app-api-stubs-docs",
-    defaults: ["metalava-api-stubs-default"],
-    libs: ["framework-all"],
-    arg_files: ["core/res/AndroidManifest.xml"],
-    args: metalava_framework_docs_args +
-        " --show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
-        "process=android.annotation.SystemApi.Process.ALL\\)" +
-        " --show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.MODULE_APPS," +
-        "process=android.annotation.SystemApi.Process.ALL\\)",
-}
+
+// The following droidstub module generates source files for the API stub library for
+// modules. Note that it not only includes its own APIs but also other APIs that have
+// narrower scope (all @SystemApis, not just the ones with 'client=MODULE_LIBRARIES').
 
 droidstubs {
     name: "module-lib-api-stubs-docs",
     defaults: ["metalava-api-stubs-default"],
     libs: ["framework-all"],
     arg_files: ["core/res/AndroidManifest.xml"],
-    args: metalava_framework_docs_args +
-        " --show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
-        "process=android.annotation.SystemApi.Process.ALL\\)" +
-        " --show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.MODULE_APPS," +
-        "process=android.annotation.SystemApi.Process.ALL\\)" +
-        " --show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
-        "process=android.annotation.SystemApi.Process.ALL\\)",
+    args: metalava_framework_docs_args + priv_apps + module_libs,
 }
 
 /////////////////////////////////////////////////////////////////////
@@ -340,21 +293,6 @@
 }
 
 java_library_static {
-    name: "android_module_app_stubs_current",
-    srcs: [
-        ":module-app-api-stubs-docs",
-    ],
-    libs: [
-        "stub-annotations",
-        "framework-all",
-    ],
-    static_libs: [
-        "private-stub-annotations-jar",
-    ],
-    defaults: ["framework-stubs-default"],
-}
-
-java_library_static {
     name: "android_module_lib_stubs_current",
     srcs: [
         ":module-lib-api-stubs-docs",
@@ -401,7 +339,7 @@
     merge_annotations_dirs: [
         "metalava-manual",
     ],
-    args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\)",
+    args: priv_apps,
 }
 
 java_library_static {
@@ -427,7 +365,7 @@
     removed_dex_api_filename: "removed-dex.txt",
     args: metalava_framework_docs_args +
         " --show-unannotated " +
-        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) " +
+        priv_apps +
         " --show-annotation android.annotation.TestApi ",
 }
 
@@ -446,7 +384,7 @@
         " --hide ReferencesHidden " +
         " --hide UnhiddenSystemApi " +
         " --show-unannotated " +
-        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) " +
+        priv_apps +
         " --show-annotation android.annotation.TestApi ",
 }
 
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 2266d04..91df098 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -99,13 +99,10 @@
     path: "java"
 }
 
-droidstubs {
-    name: "updatable-media-stubs",
-    srcs: [
-        ":updatable-media-srcs",
-        ":framework-media-annotation-srcs",
-    ],
-    defaults: [ "framework-module-stubs-defaults-systemapi" ],
+stubs_defaults {
+    name: "framework-media-stubs-srcs-defaults",
+    srcs: [ ":updatable-media-srcs" ],
+    libs: [ "framework_media_annotation" ],
     aidl: {
         // TODO(b/135922046) remove this
         include_dirs: ["frameworks/base/core/java"],
@@ -113,9 +110,53 @@
     sdk_version: "system_current",
 }
 
+droidstubs {
+    name: "framework-media-stubs-srcs-publicapi",
+    defaults: [
+        "framework-media-stubs-srcs-defaults",
+        "framework-module-stubs-defaults-publicapi",
+    ],
+}
+
+droidstubs {
+    name: "framework-media-stubs-srcs-systemapi",
+    defaults: [
+        "framework-media-stubs-srcs-defaults",
+        "framework-module-stubs-defaults-systemapi",
+    ],
+}
+
+droidstubs {
+    name: "framework-media-api-module_libs_api",
+    defaults: [
+        "framework-media-stubs-srcs-defaults",
+        "framework-module-api-defaults-module_libs_api",
+    ],
+}
+
+droidstubs {
+    name: "framework-media-stubs-srcs-module_libs_api",
+    defaults: [
+        "framework-media-stubs-srcs-defaults",
+        "framework-module-stubs-defaults-module_libs_api",
+    ],
+}
+
 java_library {
-    name: "updatable_media_stubs",
-    srcs: [":updatable-media-stubs"],
+    name: "framework-media-stubs-publicapi",
+    srcs: [":framework-media-stubs-srcs-publicapi"],
+    sdk_version: "current",
+}
+
+java_library {
+    name: "framework-media-stubs-systemapi",
+    srcs: [":framework-media-stubs-srcs-systemapi"],
+    sdk_version: "system_current",
+}
+
+java_library {
+    name: "framework-media-stubs-module_libs_api",
+    srcs: [":framework-media-stubs-srcs-module_libs_api"],
     sdk_version: "system_current",
 }
 
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index 09571a1..126fa00 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -44,23 +44,66 @@
     ],
 }
 
+stubs_defaults {
+    name: "framework-permission-stubs-defaults",
+    srcs: [ ":framework-permission-sources" ],
+    libs: [ "framework-annotations-lib" ],
+    sdk_version: "system_current",
+}
+
 droidstubs {
-    name: "framework-permission-stubs-sources",
-    srcs: [
-        ":framework-annotations",
-        ":framework-permission-sources",
+    name: "framework-permission-stubs-srcs-publicapi",
+    sdk_version: "system_current",
+    defaults: [
+        "framework-module-stubs-defaults-publicapi",
+        "framework-permission-stubs-defaults",
     ],
+}
+
+droidstubs {
+    name: "framework-permission-stubs-srcs-systemapi",
     sdk_version: "system_current",
     defaults: [
         "framework-module-stubs-defaults-systemapi",
+        "framework-permission-stubs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-permission-api-module_libs_api",
+    sdk_version: "system_current",
+    defaults: [
+        "framework-module-api-defaults-module_libs_api",
+        "framework-permission-stubs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-permission-stubs-srcs-module_libs_api",
+    sdk_version: "system_current",
+    defaults: [
+        "framework-module-stubs-defaults-module_libs_api",
+        "framework-permission-stubs-defaults",
     ],
 }
 
 java_library {
-    name: "framework-permission-stubs",
-    srcs: [
-        ":framework-permission-stubs-sources",
-    ],
+    name: "framework-permission-stubs-publicapi",
+    srcs: [ ":framework-permission-stubs-srcs-publicapi" ],
+    sdk_version: "system_current",
+    installable: false,
+}
+
+java_library {
+    name: "framework-permission-stubs-systemapi",
+    srcs: [ ":framework-permission-stubs-srcs-systemapi" ],
+    sdk_version: "system_current",
+    installable: false,
+}
+
+java_library {
+    name: "framework-permission-stubs-module_libs_api",
+    srcs: [ ":framework-permission-stubs-srcs-module_libs_api" ],
     sdk_version: "system_current",
     installable: false,
 }
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
index 5f2d944..6c7f82a 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
 import android.os.UserHandle;
 
 /**
@@ -27,7 +28,7 @@
  * TODO(b/147914847): Remove @hide when it becomes the default.
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES, process = SystemApi.Process.SYSTEM_SERVER)
+@SystemApi(client = Client.SYSTEM_SERVER)
 public interface RuntimePermissionsPersistence {
 
     /**
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java
index 2a939e5..cd2750a 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
 
 import java.util.List;
 import java.util.Map;
@@ -29,7 +30,7 @@
  * TODO(b/147914847): Remove @hide when it becomes the default.
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES, process = SystemApi.Process.SYSTEM_SERVER)
+@SystemApi(client = Client.SYSTEM_SERVER)
 public final class RuntimePermissionsState {
 
     /**
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
index 63c8eed..2908a38 100644
--- a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
+++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
 import android.os.UserHandle;
 
 /**
@@ -27,7 +28,7 @@
  * TODO(b/147914847): Remove @hide when it becomes the default.
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES, process = SystemApi.Process.SYSTEM_SERVER)
+@SystemApi(client = Client.SYSTEM_SERVER)
 public interface RolesPersistence {
 
     /**
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesState.java b/apex/permission/service/java/com/android/role/persistence/RolesState.java
index bff980e..7da9d11 100644
--- a/apex/permission/service/java/com/android/role/persistence/RolesState.java
+++ b/apex/permission/service/java/com/android/role/persistence/RolesState.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
 
 import java.util.Map;
 import java.util.Set;
@@ -29,7 +30,7 @@
  * TODO(b/147914847): Remove @hide when it becomes the default.
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES, process = SystemApi.Process.SYSTEM_SERVER)
+@SystemApi(client = Client.SYSTEM_SERVER)
 public final class RolesState {
 
     /**
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index 245a96b..86f4ab7 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -44,34 +44,68 @@
     ],
 }
 
-droidstubs {
-    name: "framework-sdkextensions-droidstubs-publicapi",
-    defaults: [
-        "framework-sdkextensions-stubs-defaults",
-        "framework-module-stubs-defaults-publicapi",
-    ]
-}
-
-droidstubs {
-    name: "framework-sdkextensions-droidstubs-systemapi",
-    defaults: [
-        "framework-sdkextensions-stubs-defaults",
-        "framework-module-stubs-defaults-systemapi",
-    ]
-}
-
 stubs_defaults {
     name: "framework-sdkextensions-stubs-defaults",
-    srcs: [
-        ":framework-sdkextensions-sources",
-        ":framework-annotations",
-    ],
+    srcs: [ ":framework-sdkextensions-sources" ],
+    libs: [ "framework-annotations-lib" ],
     sdk_version: "system_current",
 }
 
+droidstubs {
+    name: "framework-sdkextensions-stubs-srcs-publicapi",
+    defaults: [
+        "framework-module-stubs-defaults-publicapi",
+        "framework-sdkextensions-stubs-defaults",
+    ]
+}
+
+droidstubs {
+    name: "framework-sdkextensions-stubs-srcs-systemapi",
+    defaults: [
+        "framework-module-stubs-defaults-systemapi",
+        "framework-sdkextensions-stubs-defaults",
+    ]
+}
+
+droidstubs {
+    name: "framework-sdkextensions-api-module_libs_api",
+    defaults: [
+        "framework-module-api-defaults-module_libs_api",
+        "framework-sdkextensions-stubs-defaults",
+    ]
+}
+
+droidstubs {
+    name: "framework-sdkextensions-stubs-srcs-module_libs_api",
+    defaults: [
+        "framework-module-stubs-defaults-module_libs_api",
+        "framework-sdkextensions-stubs-defaults",
+    ]
+}
+
+java_library {
+    name: "framework-sdkextensions-stubs-publicapi",
+    srcs: [":framework-sdkextensions-stubs-srcs-publicapi"],
+    sdk_version: "current",
+    visibility: [
+        "//frameworks/base", // Framework
+        "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
+    ]
+}
+
 java_library {
     name: "framework-sdkextensions-stubs-systemapi",
-    srcs: [":framework-sdkextensions-droidstubs-systemapi"],
+    srcs: [":framework-sdkextensions-stubs-srcs-systemapi"],
+    sdk_version: "system_current",
+    visibility: [
+        "//frameworks/base", // Framework
+        "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
+    ]
+}
+
+java_library {
+    name: "framework-sdkextensions-stubs-module_libs_api",
+    srcs: [":framework-sdkextensions-stubs-srcs-module_libs_api"],
     sdk_version: "system_current",
     visibility: [
         "//frameworks/base", // Framework
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 231c910..d85ae69 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -30,6 +30,7 @@
     ],
     permitted_packages: [
         "android.app",
+        "android.os",
         "android.util",
     ],
     libs: [
@@ -43,6 +44,7 @@
         "//frameworks/base/apex/statsd:__subpackages__",
         //TODO(b/146167933) remove this when framework is built with framework-statsd-stubs
         "//frameworks/base",
+        "//frameworks/opt/net/wifi/service",
     ],
     apex_available: [
         "com.android.os.statsd",
@@ -50,31 +52,70 @@
     ],
 }
 
-droidstubs {
-    name: "framework-statsd-stubs-docs",
-    defaults: [
-        "framework-module-stubs-defaults-systemapi"
-    ],
-    srcs: [
-        ":framework-annotations",
-        ":framework-statsd-sources",
-    ],
+stubs_defaults {
+    name: "framework-statsd-stubs-srcs-defaults",
+    srcs: [ ":framework-statsd-sources" ],
     libs: [
         // TODO(b/148218250): Change to android_system_stubs_current
         "framework-all",
+        "framework-annotations-lib",
     ],
     sdk_version: "core_platform",
 }
 
+droidstubs {
+    name: "framework-statsd-stubs-srcs-publicapi",
+    defaults: [
+        "framework-module-stubs-defaults-systemapi",
+        "framework-statsd-stubs-srcs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-statsd-stubs-srcs-systemapi",
+    defaults: [
+        "framework-module-stubs-defaults-systemapi",
+        "framework-statsd-stubs-srcs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-statsd-api-module_libs_api",
+    defaults: [
+        "framework-module-api-defaults-module_libs_api",
+        "framework-statsd-stubs-srcs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-statsd-stubs-srcs-module_libs_api",
+    defaults: [
+        "framework-module-stubs-defaults-module_libs_api",
+        "framework-statsd-stubs-srcs-defaults",
+    ],
+}
+
+java_library {
+    name: "framework-statsd-stubs-publicapi",
+    srcs: [ ":framework-statsd-stubs-srcs-publicapi" ],
+    // TODO(b/148218250): Change to current
+    libs: [ "framework-all" ],
+    sdk_version: "core_platform",
+}
+
 // TODO(b/146167933): Use these stubs in frameworks/base/Android.bp
 java_library {
-    name: "framework-statsd-stubs",
-    srcs: [
-        ":framework-statsd-stubs-docs",
-    ],
-    libs: [
-        // TODO(b/148218250): Change to android_system_stubs_current
-        "framework-all",
-    ],
+    name: "framework-statsd-stubs-systemapi",
+    srcs: [ ":framework-statsd-stubs-srcs-systemapi" ],
+    // TODO(b/148218250): Change to system_current
+    libs: [ "framework-all" ],
+    sdk_version: "core_platform",
+}
+
+java_library {
+    name: "framework-statsd-stubs-module_libs_api",
+    srcs: [ ":framework-statsd-stubs-srcs-systemapi" ],
+    // TODO(b/148218250): Change to system_current
+    libs: [ "framework-all" ],
     sdk_version: "core_platform",
 }
diff --git a/core/java/android/os/StatsDimensionsValue.java b/apex/statsd/framework/java/android/os/StatsDimensionsValue.java
similarity index 98%
rename from core/java/android/os/StatsDimensionsValue.java
rename to apex/statsd/framework/java/android/os/StatsDimensionsValue.java
index da13ea1..886130f 100644
--- a/core/java/android/os/StatsDimensionsValue.java
+++ b/apex/statsd/framework/java/android/os/StatsDimensionsValue.java
@@ -264,7 +264,8 @@
     /**
      * Parcelable Creator for StatsDimensionsValue.
      */
-    public static final @android.annotation.NonNull Parcelable.Creator<StatsDimensionsValue> CREATOR = new
+    public static final @android.annotation.NonNull
+            Parcelable.Creator<StatsDimensionsValue> CREATOR = new
             Parcelable.Creator<StatsDimensionsValue>() {
                 public StatsDimensionsValue createFromParcel(Parcel in) {
                     return new StatsDimensionsValue(in);
diff --git a/api/current.txt b/api/current.txt
index 515df6d..a577c62 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -31293,7 +31293,7 @@
     field public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
     field public static final String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
     field public static final String ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION = "android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION";
-    field public static final String ACTION_WIFI_SCAN_AVAILABLE = "android.net.wifi.action.WIFI_SCAN_AVAILABLE";
+    field public static final String ACTION_WIFI_SCAN_AVAILABILITY_CHANGED = "android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED";
     field @Deprecated public static final int ERROR_AUTHENTICATING = 1; // 0x1
     field @Deprecated public static final String EXTRA_BSSID = "bssid";
     field public static final String EXTRA_NETWORK_INFO = "networkInfo";
diff --git a/api/module-app-current.txt b/api/module-app-current.txt
deleted file mode 100644
index d802177..0000000
--- a/api/module-app-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/api/module-app-removed.txt b/api/module-app-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/api/module-app-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index d11801b..b4259474 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -87,8 +87,8 @@
     method @NonNull public java.util.List<android.timezone.CountryTimeZones.TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long);
     method public boolean hasUtcZone(long);
     method public boolean isDefaultTimeZoneBoosted();
-    method public boolean isForCountryCode(@NonNull String);
     method @Nullable public android.timezone.CountryTimeZones.OffsetResult lookupByOffsetWithBias(int, @Nullable Boolean, @Nullable Integer, long, @Nullable android.icu.util.TimeZone);
+    method public boolean matchesCountryCode(@NonNull String);
   }
 
   public static final class CountryTimeZones.OffsetResult {
@@ -98,7 +98,7 @@
   }
 
   public static final class CountryTimeZones.TimeZoneMapping {
-    method @Nullable public android.icu.util.TimeZone getTimeZone();
+    method @NonNull public android.icu.util.TimeZone getTimeZone();
     method @NonNull public String getTimeZoneId();
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index 8630b36..89811ad 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2304,6 +2304,7 @@
     field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
     field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
     field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
+    field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000
     field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
@@ -7775,7 +7776,7 @@
   }
 
   public static interface WifiManager.ScoreChangeCallback {
-    method public void onStatusChange(int, boolean);
+    method public void onScoreChange(int, @NonNull android.net.NetworkScore);
     method public void onTriggerUpdateOfWifiUsabilityStats(int);
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index c8d746b..cee560b0 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -933,6 +933,7 @@
     field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
     field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
     field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
+    field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000
     field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 2237bf2..ebed4ee 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -110,7 +110,7 @@
     ],
 
     cflags: [
-        // "-DNEW_ENCODING_SCHEME",
+        "-DNEW_ENCODING_SCHEME",
     ],
 
     local_include_dirs: [
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index cc05dbb..fccefdc 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -378,7 +378,7 @@
         BootTimeEventUtcTime boot_time_event_utc_time_reported = 241;
         BootTimeEventErrorCode boot_time_event_error_code_reported = 242 [(module) = "framework"];
         UserspaceRebootReported userspace_reboot_reported = 243;
-        NotificationReported notification_reported = 244;
+        NotificationReported notification_reported = 244 [(module) = "framework"];
         NotificationPanelReported notification_panel_reported = 245;
         NotificationChannelModified notification_panel_modified = 246;
         IntegrityCheckResultReported integrity_check_result_reported = 247 [(module) = "framework"];
@@ -4247,6 +4247,9 @@
         // Time since last factory reset.
         // Logged from bootstat.
         FACTORY_RESET_TIME_SINCE_RESET = 18;
+        // Init's total time spent for completing the 1st stage.
+        // Logged from bootstat.
+        ANDROID_INIT_STAGE_1 = 19;
     }
 
     // Type of the event.
@@ -4274,19 +4277,19 @@
         // BOOT_COMPLETE for device with no encryption.
         BOOT_COMPLETE_NO_ENCRYPTION = 4;
         // Adjusted BOOT_COMPLETE for encrypted device extracting decryption time.
-        BOOT_COMPLETE_POST_DESCRYPT = 5;
+        BOOT_COMPLETE_POST_DECRYPT = 5;
         // BOOT_COMPLETE after factory reset.
         FACTORY_RESET_BOOT_COMPLETE = 6;
         // BOOT_COMPLETE_NO_ENCRYPTION after factory reset.
         FACTORY_RESET_BOOT_COMPLETE_NO_ENCRYPTION = 7;
-        // BOOT_COMPLETE_POST_DESCRYPT after factory reset.
-        FACTORY_RESET_BOOT_COMPLETE_POST_DESCRYPT = 8;
+        // BOOT_COMPLETE_POST_DECRYPT after factory reset.
+        FACTORY_RESET_BOOT_COMPLETE_POST_DECRYPT = 8;
         // BOOT_COMPLETE after OTA.
         OTA_BOOT_COMPLETE = 9;
         // BOOT_COMPLETE_NO_ENCRYPTION after OTA.
         OTA_BOOT_COMPLETE_NO_ENCRYPTION = 10;
-        // BOOT_COMPLETE_POST_DESCRYPT after OTA.
-        OTA_BOOT_COMPLETE_POST_DESCRYPT = 11;
+        // BOOT_COMPLETE_POST_DECRYPT after OTA.
+        OTA_BOOT_COMPLETE_POST_DECRYPT = 11;
         // Time when the system starts sending LOCKED_BOOT_COMPLETED broadcast.
         // Logged from  f/b/services/.../UserController.java
         FRAMEWORK_LOCKED_BOOT_COMPLETED = 12;
@@ -6479,6 +6482,8 @@
         USER_DENIED_WITH_PREJUDICE_IN_SETTINGS = 14;
         // permission was automatically revoked after one-time permission expired
         AUTO_ONE_TIME_PERMISSION_REVOKED = 15;
+        // permission was automatically revoked for unused app
+        AUTO_UNUSED_APP_PERMISSION_REVOKED = 16;
     }
     // The result of the permission grant
     optional Result result = 6;
@@ -7556,6 +7561,28 @@
 
     // The result of the permission grant
     optional bool permission_granted = 6;
+
+    // State of Permission Flags after grant as per android.content.pm.PermissionFlags
+    optional int32 permission_flags = 7;
+
+    enum Button {
+        UNDEFINED = 0;
+        // Allow button
+        ALLOW = 1;
+        // Deny button
+        DENY = 2;
+        // Ask every time button
+        ASK_EVERY_TIME = 3;
+        // Allow all the time button
+        ALLOW_ALWAYS = 4;
+        // Allow only while using the app button
+        ALLOW_FOREGROUND = 5;
+        // Same is Deny button but shown in while in use dialog
+        DENY_FOREGROUND = 6;
+    }
+
+    // Button pressed in the dialog
+    optional Button button_pressed = 8;
 }
 
 /**
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 83983e8..5c606bc 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -547,14 +547,11 @@
 void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
                                                    const int64_t& nextBucketStartTimeNs) {
     int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
+    int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
 
     GaugeBucket info;
     info.mBucketStartNs = mCurrentBucketStartTimeNs;
-    if (eventTimeNs < fullBucketEndTimeNs) {
-        info.mBucketEndNs = eventTimeNs;
-    } else {
-        info.mBucketEndNs = fullBucketEndTimeNs;
-    }
+    info.mBucketEndNs = bucketEndTime;
 
     // Add bucket to mPastBuckets if bucket is large enough.
     // Otherwise, drop the bucket data and add bucket metadata to mSkippedBuckets.
@@ -569,7 +566,7 @@
         }
     } else {
         mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
-        mCurrentSkippedBucket.bucketEndTimeNs = eventTimeNs;
+        mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
         if (!maxDropEventsReached()) {
             mCurrentSkippedBucket.dropEvents.emplace_back(
                     buildDropEvent(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL));
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 2a5b530..dc9b413 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -379,9 +379,6 @@
     if (!mCurrentBucketIsInvalid) {
         // Only report to StatsdStats once per invalid bucket.
         StatsdStats::getInstance().noteInvalidatedBucket(mMetricId);
-
-        mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
-        mCurrentSkippedBucket.bucketEndTimeNs = getCurrentBucketEndTimeNs();
     }
 
     if (!maxDropEventsReached()) {
@@ -955,12 +952,6 @@
     int64_t conditionTrueDuration = mConditionTimer.newBucketStart(bucketEndTime);
     bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs;
     if (!isBucketLargeEnough) {
-        // If the bucket is valid, this is the only drop reason and we need to
-        // set the skipped bucket start and end times.
-        if (!mCurrentBucketIsInvalid) {
-            mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
-            mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
-        }
         if (!maxDropEventsReached()) {
             mCurrentSkippedBucket.dropEvents.emplace_back(
                     buildDropEvent(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL));
@@ -978,6 +969,8 @@
             }
         }
     } else {
+        mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
+        mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
         mSkippedBuckets.emplace_back(mCurrentSkippedBucket);
     }
 
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 92e8241..f6245ac 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -3333,7 +3333,7 @@
 
     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
               report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+    EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40),
               report.value_metrics().skipped(0).end_bucket_elapsed_millis());
     EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
 
@@ -3393,7 +3393,7 @@
 
     EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
               report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-    EXPECT_EQ(NanoToMillis(bucket3StartTimeNs),
+    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
               report.value_metrics().skipped(0).end_bucket_elapsed_millis());
     EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
 
@@ -3470,7 +3470,7 @@
 
     EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
               report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-    EXPECT_EQ(NanoToMillis(bucket3StartTimeNs),
+    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
               report.value_metrics().skipped(0).end_bucket_elapsed_millis());
     EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
 
@@ -3519,7 +3519,8 @@
     // Check dump report.
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer->onDumpReport(bucketStartTimeNs + 100, true /* include recent buckets */, true,
+    int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
+    valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
                                 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
 
     StatsLogReport report = outputStreamToProto(&output);
@@ -3529,13 +3530,13 @@
 
     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
               report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+    EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
               report.value_metrics().skipped(0).end_bucket_elapsed_millis());
     EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
 
     auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
     EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
-    EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 100), dropEvent.drop_time_millis());
+    EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
 }
 
 /*
@@ -3569,7 +3570,8 @@
     // Check dump report.
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer->onDumpReport(bucketStartTimeNs + 100, true /* include recent buckets */, true,
+    int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
+    valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
                                 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
 
     StatsLogReport report = outputStreamToProto(&output);
@@ -3579,13 +3581,13 @@
 
     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
               report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+    EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
               report.value_metrics().skipped(0).end_bucket_elapsed_millis());
     EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
 
     auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
     EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
-    EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 100), dropEvent.drop_time_millis());
+    EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
 }
 
 /*
@@ -3691,8 +3693,9 @@
     // Check dump report.
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer->onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */,
-                                true, NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+    int64_t dumpReportTimeNs = bucketStartTimeNs + 9000000;
+    valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
+                                NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
 
     StatsLogReport report = outputStreamToProto(&output);
     EXPECT_TRUE(report.has_value_metrics());
@@ -3701,13 +3704,13 @@
 
     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
               report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-    EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000),
+    EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
               report.value_metrics().skipped(0).end_bucket_elapsed_millis());
     EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
 
     auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
     EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
-    EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis());
+    EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
 }
 
 /*
@@ -3739,7 +3742,8 @@
     // Check dump report.
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer->onDumpReport(bucketStartTimeNs + 1000, true /* include recent buckets */, true,
+    int64_t dumpReportTimeNs = bucketStartTimeNs + 1000;
+    valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
                                 FAST /* dumpLatency */, &strSet, &output);
 
     StatsLogReport report = outputStreamToProto(&output);
@@ -3749,7 +3753,7 @@
 
     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
               report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+    EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
               report.value_metrics().skipped(0).end_bucket_elapsed_millis());
     EXPECT_EQ(2, report.value_metrics().skipped(0).drop_event_size());
 
@@ -3759,7 +3763,7 @@
 
     dropEvent = report.value_metrics().skipped(0).drop_event(1);
     EXPECT_EQ(BucketDropReason::DUMP_REPORT_REQUESTED, dropEvent.drop_reason());
-    EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 1000), dropEvent.drop_time_millis());
+    EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
 }
 
 /*
@@ -3826,6 +3830,7 @@
     // Check dump report.
     ProtoOutputStream output;
     std::set<string> strSet;
+    int64_t dumpReportTimeNs = bucketStartTimeNs + 1000;
     // Because we already have 10 dump events in the current bucket,
     // this case should not be added to the list of dump events.
     valueProducer->onDumpReport(bucketStartTimeNs + 1000, true /* include recent buckets */, true,
@@ -3838,7 +3843,7 @@
 
     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
               report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+    EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
               report.value_metrics().skipped(0).end_bucket_elapsed_millis());
     EXPECT_EQ(10, report.value_metrics().skipped(0).drop_event_size());
 
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 4e47594..c60f7bd 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -278,7 +278,7 @@
             String resolvedType, boolean fgRequired, String callingPackage, @UserIdInt int userId,
             boolean allowBackgroundActivityStarts) throws TransactionTooLargeException;
 
-    public abstract void disconnectActivityFromServices(Object connectionHolder, Object conns);
+    public abstract void disconnectActivityFromServices(Object connectionHolder);
     public abstract void cleanUpServices(@UserIdInt int userId, ComponentName component,
             Intent baseIntent);
     public abstract ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, @UserIdInt int userId);
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 9958c6a..c13c5a5 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -21,6 +21,7 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
+import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.util.function.HexFunction;
 import com.android.internal.util.function.QuadFunction;
 
@@ -91,4 +92,16 @@
      */
     public abstract void updateAppWidgetVisibility(SparseArray<String> uidPackageNames,
             boolean visible);
+
+    /**
+     * Like {@link AppOpsManager#setUidMode}, but allows ignoring a certain callback.
+     */
+    public abstract void setUidModeIgnoringCallback(int code, int uid, int mode,
+            @Nullable IAppOpsCallback callbackToIgnore);
+
+    /**
+     * Like {@link AppOpsManager#setMode}, but allows ignoring a certain callback.
+     */
+    public abstract void setModeIgnoringCallback(int code, int uid, @NonNull String packageName,
+            int mode, @Nullable IAppOpsCallback callbackToIgnore);
 }
diff --git a/core/java/android/app/prediction/AppPredictionContext.java b/core/java/android/app/prediction/AppPredictionContext.java
index d14238b..8fc7e8d 100644
--- a/core/java/android/app/prediction/AppPredictionContext.java
+++ b/core/java/android/app/prediction/AppPredictionContext.java
@@ -100,6 +100,16 @@
                 && mPackageName.equals(other.mPackageName);
     }
 
+    @NonNull
+    @Override
+    public String toString() {
+        return new StringBuilder(this.getClass().getSimpleName())
+                .append("[mUiSurface=").append(mUiSurface)
+                .append(",mPackageName=").append(mPackageName)
+                .append(",mPredictedTargetCount=").append(mPredictedTargetCount)
+                .append(",mExtras=").append(mExtras.toString()).append("]").toString();
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b64c001..6d5e8fb 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1506,6 +1506,15 @@
      */
     public static final int INSTALL_FAILED_WRONG_INSTALLED_VERSION = -121;
 
+    /**
+     * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+     * if the new package failed because it contains a request to use a process that was not
+     * explicitly defined as part of its &lt;processes&gt; tag.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_PROCESS_NOT_DEFINED = -122;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DELETE_" }, value = {
             DELETE_KEEP_DATA,
@@ -7237,6 +7246,7 @@
             case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT";
             case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE";
             case INSTALL_FAILED_WRONG_INSTALLED_VERSION: return "INSTALL_FAILED_WRONG_INSTALLED_VERSION";
+            case INSTALL_FAILED_PROCESS_NOT_DEFINED: return "INSTALL_FAILED_PROCESS_NOT_DEFINED";
             default: return Integer.toString(status);
         }
     }
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index a0f089b..3aa1a6d 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -259,6 +259,17 @@
     @TestApi
     public static final int PROTECTION_FLAG_COMPANION = 0x800000;
 
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>retailDemo</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_RETAIL_DEMO = 0x1000000;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
             PROTECTION_FLAG_PRIVILEGED,
@@ -282,6 +293,7 @@
             PROTECTION_FLAG_APP_PREDICTOR,
             PROTECTION_FLAG_TELEPHONY,
             PROTECTION_FLAG_COMPANION,
+            PROTECTION_FLAG_RETAIL_DEMO,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProtectionFlags {}
@@ -528,6 +540,9 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_TELEPHONY) != 0) {
             protLevel += "|telephony";
         }
+        if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) {
+            protLevel += "|retailDemo";
+        }
         return protLevel;
     }
 
diff --git a/core/java/android/content/pm/parsing/ComponentParseUtils.java b/core/java/android/content/pm/parsing/ComponentParseUtils.java
index 9a0a6d5..a0f5812 100644
--- a/core/java/android/content/pm/parsing/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/ComponentParseUtils.java
@@ -3414,16 +3414,12 @@
             proc.name = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestProcess_process,0);
             proc.name = PackageParser.buildProcessName(parsingPackage.getPackageName(),
-                    null, proc.name, flags, separateProcesses, outError);
-
-            if (proc.name == null || proc.name.length() <= 0) {
-                outError[0] = "<process> does not specify android:process";
+                    parsingPackage.getPackageName(), proc.name, flags, separateProcesses, outError);
+            if (outError[0] != null) {
                 return null;
             }
-            proc.name = PackageParser.buildProcessName(parsingPackage.getPackageName(),
-                    parsingPackage.getPackageName(), proc.name,
-                    flags, separateProcesses, outError);
-            if (outError[0] != null) {
+            if (proc.name == null || proc.name.length() <= 0) {
+                outError[0] = "<process> does not specify android:process";
                 return null;
             }
         } finally {
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 4a66879..987a53e 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -17,24 +17,24 @@
 package android.os.incremental;
 
 /**
- * Set up files and directories used in an installation session.
- * Currently only used by Incremental Installation.
- * For Incremental installation, the expected outcome of this function is:
- * 0) All the files are in defaultStorage
- * 1) All APK files are in the same directory, bound to mApkStorage, and bound to the
- * InstallerSession's stage dir. The files are linked from mApkStorage to defaultStorage.
- * 2) All lib files are in the sub directories as their names suggest, and in the same parent
- * directory as the APK files. The files are linked from mApkStorage to defaultStorage.
- * 3) OBB files are in another directory that is different from APK files and lib files, bound
- * to mObbStorage. The files are linked from mObbStorage to defaultStorage.
+ * Set up files and directories used in an installation session. Currently only used by Incremental
+ * Installation. For Incremental installation, the expected outcome of this function is: 0) All the
+ * files are in defaultStorage 1) All APK files are in the same directory, bound to mApkStorage, and
+ * bound to the InstallerSession's stage dir. The files are linked from mApkStorage to
+ * defaultStorage. 2) All lib files are in the sub directories as their names suggest, and in the
+ * same parent directory as the APK files. The files are linked from mApkStorage to defaultStorage.
+ * 3) OBB files are in another directory that is different from APK files and lib files, bound to
+ * mObbStorage. The files are linked from mObbStorage to defaultStorage.
  *
  * @throws IllegalStateException the session is not an Incremental installation session.
  */
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.Context;
 import android.content.pm.DataLoaderParams;
 import android.content.pm.InstallationFile;
+import android.text.TextUtils;
 import android.util.Slog;
 
 import java.io.File;
@@ -42,6 +42,8 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.List;
+import java.util.Objects;
 import java.util.Random;
 
 /**
@@ -50,6 +52,10 @@
  */
 public final class IncrementalFileStorages {
     private static final String TAG = "IncrementalFileStorages";
+
+    private static final String TMP_DIR_ROOT = "/data/incremental/tmp";
+    private static final Random TMP_DIR_RANDOM = new Random();
+
     private @Nullable IncrementalStorage mDefaultStorage;
     private @Nullable String mDefaultDir;
     private @NonNull IncrementalManager mIncrementalManager;
@@ -61,41 +67,77 @@
      * TODO(b/133435829): code clean up
      *
      * @throws IllegalStateException the session is not an Incremental installation session.
+     * @throws IOException if fails to setup files or directories.
      */
-    public IncrementalFileStorages(@NonNull String packageName,
+    public static IncrementalFileStorages initialize(Context context,
             @NonNull File stageDir,
+            @NonNull DataLoaderParams dataLoaderParams,
+            List<InstallationFile> addedFiles) throws IOException {
+        // TODO(b/136132412): sanity check if session should not be incremental
+        IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
+                Context.INCREMENTAL_SERVICE);
+        if (incrementalManager == null) {
+            // TODO(b/146080380): add incremental-specific error code
+            throw new IOException("Failed to obtain incrementalManager.");
+        }
+
+        IncrementalFileStorages result = null;
+        try {
+            result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams);
+            for (InstallationFile file : addedFiles) {
+                if (file.getFileType() == InstallationFile.FILE_TYPE_APK) {
+                    try {
+                        result.addApkFile(file);
+                    } catch (IOException e) {
+                        // TODO(b/146080380): add incremental-specific error code
+                        throw new IOException(
+                                "Failed to add and configure Incremental File: " + file.getName(),
+                                e);
+                    }
+                } else {
+                    throw new IOException("Unknown file type: " + file.getFileType());
+                }
+            }
+
+            if (!result.mDefaultStorage.startLoading()) {
+                // TODO(b/146080380): add incremental-specific error code
+                throw new IOException("Failed to start loading data for Incremental installation.");
+            }
+
+            return result;
+        } catch (IOException e) {
+            if (result != null) {
+                result.cleanUp();
+            }
+            throw e;
+        }
+    }
+
+    private IncrementalFileStorages(@NonNull File stageDir,
             @NonNull IncrementalManager incrementalManager,
-            @NonNull DataLoaderParams dataLoaderParams) {
+            @NonNull DataLoaderParams dataLoaderParams) throws IOException {
         mStageDir = stageDir;
         mIncrementalManager = incrementalManager;
         if (dataLoaderParams.getComponentName().getPackageName().equals("local")) {
             final String incrementalPath = dataLoaderParams.getArguments();
-            mDefaultStorage = mIncrementalManager.openStorage(incrementalPath);
             mDefaultDir = incrementalPath;
-            return;
-        }
-        mDefaultDir = getTempDir();
-        if (mDefaultDir == null) {
-            return;
-        }
-        mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir,
-                dataLoaderParams,
-                IncrementalManager.CREATE_MODE_CREATE
-                        | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false);
-    }
-
-    /**
-     * Adds a file into the installation session. Makes sure it will be placed inside
-     * a proper storage instance, based on its file type.
-     */
-    public void addFile(@NonNull InstallationFile file) throws IOException {
-        if (mDefaultStorage == null) {
-            throw new IOException("Cannot add file because default storage does not exist");
-        }
-        if (file.getFileType() == InstallationFile.FILE_TYPE_APK) {
-            addApkFile(file);
+            if (TextUtils.isEmpty(mDefaultDir)) {
+                throw new IOException("Failed to create storage: incrementalPath is empty");
+            }
+            mDefaultStorage = mIncrementalManager.openStorage(incrementalPath);
         } else {
-            throw new IOException("Unknown file type: " + file.getFileType());
+            mDefaultDir = getTempDir();
+            if (mDefaultDir == null) {
+                throw new IOException("Failed to create storage: tempDir is empty");
+            }
+            mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir,
+                    dataLoaderParams,
+                    IncrementalManager.CREATE_MODE_CREATE
+                            | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false);
+        }
+
+        if (mDefaultStorage == null) {
+            throw new IOException("Failed to create storage");
         }
     }
 
@@ -108,26 +150,6 @@
             mDefaultStorage.makeFile(apkName, apk.getSize(), null,
                     apk.getMetadata(), 0, null, null, null);
         }
-        if (targetFile.exists()) {
-            Slog.i(TAG, "!!! created: " + targetFile.getAbsolutePath());
-        }
-    }
-
-    /**
-     * Starts loading data for default storage.
-     * TODO(b/136132412): update the implementation with latest API design.
-     */
-    public boolean startLoading() {
-        if (mDefaultStorage == null) {
-            return false;
-        }
-        return mDefaultStorage.startLoading();
-    }
-
-    /**
-     * Sets up obb storage directory and create bindings.
-     */
-    public void finishSetUp() {
     }
 
     /**
@@ -135,22 +157,21 @@
      * TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept
      */
     public void cleanUp() {
-        if (mDefaultStorage != null && mDefaultDir != null) {
-            try {
-                mDefaultStorage.unBind(mDefaultDir);
-                mDefaultStorage.unBind(mStageDir.getAbsolutePath());
-            } catch (IOException ignored) {
-            }
-            mDefaultDir = null;
-            mDefaultStorage = null;
+        Objects.requireNonNull(mDefaultStorage);
+
+        try {
+            mDefaultStorage.unBind(mDefaultDir);
+            mDefaultStorage.unBind(mStageDir.getAbsolutePath());
+        } catch (IOException ignored) {
         }
+
+        mDefaultDir = null;
+        mDefaultStorage = null;
     }
 
-    private String getTempDir() {
-        final String tmpDirRoot = "/data/incremental/tmp";
-        final Random random = new Random();
-        final Path tmpDir =
-                Paths.get(tmpDirRoot, String.valueOf(random.nextInt(Integer.MAX_VALUE - 1)));
+    private static String getTempDir() {
+        final Path tmpDir = Paths.get(TMP_DIR_ROOT,
+                String.valueOf(TMP_DIR_RANDOM.nextInt(Integer.MAX_VALUE - 1)));
         try {
             Files.createDirectories(tmpDir);
         } catch (Exception ex) {
diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
index 5875761..970acd0 100644
--- a/core/java/android/timezone/CountryTimeZones.java
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -51,8 +51,10 @@
         }
 
         /**
-         * Returns the ID for this mapping. See also {@link #getTimeZone()} which handles when the
-         * ID is unrecognized.
+         * Returns the ID for this mapping. The ID is a tzdb time zone identifier like
+         * "America/Los_Angeles" that can be used with methods such as {@link
+         * TimeZone#getFrozenTimeZone(String)}. See {@link #getTimeZone()} which returns a frozen
+         * {@link TimeZone} object.
          */
         @NonNull
         public String getTimeZoneId() {
@@ -60,10 +62,9 @@
         }
 
         /**
-         * Returns a {@link TimeZone} object for this mapping, or {@code null} if the ID is
-         * unrecognized.
+         * Returns a frozen {@link TimeZone} object for this mapping.
          */
-        @Nullable
+        @NonNull
         public TimeZone getTimeZone() {
             return mDelegate.getTimeZone();
         }
@@ -158,9 +159,10 @@
     }
 
     /**
-     * Returns true if the ISO code for the country is a match for the one specified.
+     * Returns true if the ISO code for the country is a case-insensitive match for the one
+     * supplied.
      */
-    public boolean isForCountryCode(@NonNull String countryIso) {
+    public boolean matchesCountryCode(@NonNull String countryIso) {
         return mDelegate.isForCountryCode(countryIso);
     }
 
@@ -183,15 +185,25 @@
     }
 
     /**
-     * Qualifier for a country's default time zone. {@code true} indicates whether the default
-     * would be a good choice <em>generally</em> when there's no other information available.
+     * Qualifier for a country's default time zone. {@code true} indicates that the country's
+     * default time zone would be a good choice <em>generally</em> when there's no UTC offset
+     * information available. This will only be {@code true} in countries with multiple zones where
+     * a large majority of the population is covered by only one of them.
      */
     public boolean isDefaultTimeZoneBoosted() {
         return mDelegate.isDefaultTimeZoneBoosted();
     }
 
     /**
-     * Returns true if the country has at least one zone that is the same as UTC at the given time.
+     * Returns {@code true} if the country has at least one time zone that uses UTC at the given
+     * time. This is an efficient check when trying to validate received UTC offset information.
+     * For example, there are situations when a detected zero UTC offset cannot be distinguished
+     * from "no information available" or a corrupted signal. This method is useful because checking
+     * offset information for large countries is relatively expensive but it is generally only the
+     * countries close to the prime meridian that use UTC at <em>any</em> time of the year.
+     *
+     * @param whenMillis the time the offset information is for in milliseconds since the beginning
+     *     of the Unix epoch
      */
     public boolean hasUtcZone(long whenMillis) {
         return mDelegate.hasUtcZone(whenMillis);
diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java
new file mode 100644
index 0000000..a44ed59
--- /dev/null
+++ b/core/java/android/util/RotationUtils.java
@@ -0,0 +1,72 @@
+/*
+ * 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 android.util;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import android.graphics.Insets;
+import android.view.Surface.Rotation;
+
+/**
+ * A class containing utility methods related to rotation.
+ *
+ * @hide
+ */
+public class RotationUtils {
+
+    /**
+     * Rotates an Insets according to the given rotation.
+     */
+    public static Insets rotateInsets(Insets insets, @Rotation int rotation) {
+        if (insets == null || insets == Insets.NONE) {
+            return insets;
+        }
+        Insets rotated;
+        switch (rotation) {
+            case ROTATION_0:
+                rotated = insets;
+                break;
+            case ROTATION_90:
+                rotated = Insets.of(
+                        insets.top,
+                        insets.right,
+                        insets.bottom,
+                        insets.left);
+                break;
+            case ROTATION_180:
+                rotated = Insets.of(
+                        insets.right,
+                        insets.bottom,
+                        insets.left,
+                        insets.top);
+                break;
+            case ROTATION_270:
+                rotated = Insets.of(
+                        insets.bottom,
+                        insets.left,
+                        insets.top,
+                        insets.right);
+                break;
+            default:
+                throw new IllegalArgumentException("unknown rotation: " + rotation);
+        }
+        return rotated;
+    }
+}
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 8d58ee8..67e88a5 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -253,4 +253,12 @@
     public void setNextServedView(View view) {
         mNextServedView = view;
     }
+
+    /**
+     * Indicates whether the view's window has IME focused.
+     */
+    @UiThread
+    boolean hasImeFocus() {
+        return mHasImeFocus;
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7047586..a407bd8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14470,7 +14470,7 @@
      * @hide
      */
     public boolean hasImeFocus() {
-        return mAttachInfo != null && mAttachInfo.mHasImeFocus;
+        return getViewRootImpl() != null && getViewRootImpl().getImeFocusController().hasImeFocus();
     }
 
     /**
@@ -28725,11 +28725,6 @@
         boolean mHasWindowFocus;
 
         /**
-         * Indicates whether the view's window has IME focused.
-         */
-        boolean mHasImeFocus;
-
-        /**
          * The current visibility of the window.
          */
         int mWindowVisibility;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 435c911..4e3050c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3145,8 +3145,7 @@
             }
 
             mAttachInfo.mHasWindowFocus = hasWindowFocus;
-            mAttachInfo.mHasImeFocus = mImeFocusController.updateImeFocusable(
-                    mWindowAttributes, true /* force */);
+            mImeFocusController.updateImeFocusable(mWindowAttributes, true /* force */);
             mImeFocusController.onPreWindowFocus(hasWindowFocus, mWindowAttributes);
 
             if (mView != null) {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 307abd2..f0c16aa 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -671,6 +671,11 @@
                 mWindowFocusGainFuture = null;
             }
             synchronized (mH) {
+                if (mCurRootView != null) {
+                    // Reset the last served view and restart window focus state of the root view.
+                    mCurRootView.getImeFocusController().setServedView(null);
+                    mRestartOnNextWindowFocus = true;
+                }
                 mCurRootView = rootView;
             }
         }
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 57b63a7..50da6ce 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -854,6 +854,8 @@
         // The z of the magnifier surface, defining its z order in the list of
         // siblings having the same parent surface (usually the main app surface).
         private static final int SURFACE_Z = 5;
+        // The width of the ramp region in pixels on the left & right sides of the fish-eye effect.
+        private static final int FISHEYE_RAMP_WIDTH = 30;
 
         // Display associated to the view the magnifier is attached to.
         private final Display mDisplay;
@@ -906,7 +908,8 @@
         // Whether is in the new magnifier style.
         private boolean mIsFishEyeStyle;
         // The mesh matrix for the fish-eye effect.
-        private float[] mMesh;
+        private float[] mMeshLeft;
+        private float[] mMeshRight;
         private int mMeshWidth;
         private int mMeshHeight;
 
@@ -986,29 +989,29 @@
         }
 
         private void createMeshMatrixForFishEyeEffect() {
-            mMeshWidth = mZoom < 1.5f ? 5 : 4;
+            mMeshWidth = 1;
             mMeshHeight = 6;
             final float w = mContentWidth;
             final float h = mContentHeight;
-            final float dx = (w - mZoom * w * (mMeshWidth - 2) / mMeshWidth) / 2;
-            mMesh = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
+            final float h0 = h / mZoom;
+            final float dh = h - h0;
+            final float ramp = FISHEYE_RAMP_WIDTH;
+            mMeshLeft = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
+            mMeshRight = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
             for (int i = 0; i < 2 * (mMeshWidth + 1) * (mMeshHeight + 1); i += 2) {
                 // Calculates X value.
                 final int colIndex = i % (2 * (mMeshWidth + 1)) / 2;
-                if (colIndex == 0) {
-                    mMesh[i] = 0;
-                } else if (colIndex == mMeshWidth) {
-                    mMesh[i] = w;
-                } else {
-                    mMesh[i] = (colIndex - 1) * (w - 2 * dx) / (mMeshWidth - 2) + dx;
-                }
+                mMeshLeft[i] = (float) colIndex * ramp / mMeshWidth;
+                mMeshRight[i] = w - ramp + colIndex * ramp / mMeshWidth;
+
                 // Calculates Y value.
                 final int rowIndex = i / 2 / (mMeshWidth + 1);
-                final float y0 = colIndex == 0 || colIndex == mMeshWidth
-                        ? (h - h / mZoom) / 2 : 0;
-                final float dy = colIndex == 0 || colIndex == mMeshWidth
-                        ? h / mZoom / mMeshHeight : h / mMeshHeight;
-                mMesh[i + 1] = y0 + rowIndex * dy;
+                final float hl = h0 + dh * colIndex / mMeshWidth;
+                final float yl = (h - hl) / 2;
+                mMeshLeft[i + 1] = yl + hl * rowIndex / mMeshHeight;
+                final float hr = h - dh * colIndex / mMeshWidth;
+                final float yr = (h - hr) / 2;
+                mMeshRight[i + 1] = yr + hr * rowIndex / mMeshHeight;
             }
         }
 
@@ -1166,14 +1169,31 @@
                 final RecordingCanvas canvas =
                         mBitmapRenderNode.beginRecording(mContentWidth, mContentHeight);
                 try {
+                    final int w = mBitmap.getWidth();
+                    final int h = mBitmap.getHeight();
+                    final Paint paint = new Paint();
+                    paint.setFilterBitmap(true);
                     if (mIsFishEyeStyle) {
+                        final int ramp = FISHEYE_RAMP_WIDTH;
+                        final int margin =
+                            (int)((mContentWidth - (mContentWidth - 2 * ramp) / mZoom) / 2);
+
+                        // Draws the middle part.
+                        final Rect srcRect = new Rect(margin, 0, w - margin, h);
+                        final Rect dstRect = new Rect(
+                            ramp, 0, mContentWidth - ramp, mContentHeight);
+                        canvas.drawBitmap(mBitmap, srcRect, dstRect, paint);
+
+                        // Draws the left/right parts with mesh matrixes.
                         canvas.drawBitmapMesh(
-                                mBitmap, mMeshWidth, mMeshHeight, mMesh, 0, null, 0, null);
+                                Bitmap.createBitmap(mBitmap, 0, 0, margin, h),
+                                mMeshWidth, mMeshHeight, mMeshLeft, 0, null, 0, paint);
+                        canvas.drawBitmapMesh(
+                                Bitmap.createBitmap(mBitmap, w - margin, 0, margin, h),
+                                mMeshWidth, mMeshHeight, mMeshRight, 0, null, 0, paint);
                     } else {
-                        final Rect srcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+                        final Rect srcRect = new Rect(0, 0, w, h);
                         final Rect dstRect = new Rect(0, 0, mContentWidth, mContentHeight);
-                        final Paint paint = new Paint();
-                        paint.setFilterBitmap(true);
                         canvas.drawBitmap(mBitmap, srcRect, dstRect, paint);
                     }
                 } finally {
diff --git a/core/java/com/android/ims/internal/uce/common/CapInfo.java b/core/java/com/android/ims/internal/uce/common/CapInfo.java
index 2bb3f1f..a7a90f6 100644
--- a/core/java/com/android/ims/internal/uce/common/CapInfo.java
+++ b/core/java/com/android/ims/internal/uce/common/CapInfo.java
@@ -78,6 +78,10 @@
     private boolean mChatbotSupported = false;
     /** Chatbot role support. */
     private boolean mChatbotRoleSupported = false;
+    /** Standalone Chatbot communication support. */
+    private boolean mSmChatbotSupported = false;
+    /** MMtel based call composer support. */
+    private boolean mMmtelCallComposerSupported = false;
     /** List of supported extensions. */
     private String[] mExts = new String[10];
     /** Time used to compute when to query again. */
@@ -498,6 +502,34 @@
         this.mChatbotRoleSupported = chatbotRoleSupported;
     }
 
+    /**
+     * Checks whether standalone chatbot communication is supported.
+     */
+    public boolean isSmChatbotSupported() {
+        return mSmChatbotSupported;
+    }
+
+    /**
+     * Sets standalone chatbot communication as supported or not supported.
+     */
+    public void setSmChatbotSupported(boolean smChatbotSupported) {
+        this.mSmChatbotSupported = smChatbotSupported;
+    }
+
+    /**
+     * Checks whether Mmtel based call composer is supported.
+     */
+    public boolean isMmtelCallComposerSupported() {
+        return mMmtelCallComposerSupported;
+    }
+
+    /**
+     * Sets Mmtel based call composer as supported or not supported.
+     */
+    public void setMmtelCallComposerSupported(boolean mmtelCallComposerSupported) {
+        this.mMmtelCallComposerSupported = mmtelCallComposerSupported;
+    }
+
     /** Gets the list of supported extensions. */
     public String[] getExts() {
         return mExts;
@@ -553,6 +585,8 @@
         dest.writeInt(mSharedSketchSupported ? 1 : 0);
         dest.writeInt(mChatbotSupported ? 1 : 0);
         dest.writeInt(mChatbotRoleSupported ? 1 : 0);
+        dest.writeInt(mSmChatbotSupported ? 1 : 0);
+        dest.writeInt(mMmtelCallComposerSupported ? 1 : 0);
 
         dest.writeInt(mRcsIpVoiceCallSupported ? 1 : 0);
         dest.writeInt(mRcsIpVideoCallSupported ? 1 : 0);
@@ -602,6 +636,8 @@
         mSharedSketchSupported = (source.readInt() == 0) ? false : true;
         mChatbotSupported = (source.readInt() == 0) ? false : true;
         mChatbotRoleSupported = (source.readInt() == 0) ? false : true;
+        mSmChatbotSupported = (source.readInt() == 0) ? false : true;
+        mMmtelCallComposerSupported = (source.readInt() == 0) ? false : true;
 
         mRcsIpVoiceCallSupported = (source.readInt() == 0) ? false : true;
         mRcsIpVideoCallSupported = (source.readInt() == 0) ? false : true;
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 384275f..6a6a60d 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -39,6 +39,8 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.res.TypedArray;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Bundle;
@@ -74,6 +76,8 @@
  */
 public class AccessibilityButtonChooserActivity extends Activity {
     private static final char SERVICES_SEPARATOR = ':';
+    private static final float DISABLED_ALPHA = 0.5f;
+    private static final float ENABLED_ALPHA = 1.0f;
     private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
             new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR);
     @ShortcutType
@@ -452,9 +456,16 @@
             final boolean isLaunchMenuMode = (mShortcutMenuMode == ShortcutMenuMode.LAUNCH);
             final boolean isHardwareButtonTriggered =
                     (mShortcutButtonType == ACCESSIBILITY_SHORTCUT_KEY);
+            final boolean enabledState = (isLaunchMenuMode || isHardwareButtonTriggered);
+            final ColorMatrix grayScaleMatrix = new ColorMatrix();
+            grayScaleMatrix.setSaturation(/* grayScale */0);
 
-            holder.mLabelView.setEnabled(isLaunchMenuMode || isHardwareButtonTriggered);
-            holder.mViewItem.setEnabled(isLaunchMenuMode || isHardwareButtonTriggered);
+            holder.mIconView.setColorFilter(enabledState
+                    ? null : new ColorMatrixColorFilter(grayScaleMatrix));
+            holder.mIconView.setAlpha(enabledState
+                    ? ENABLED_ALPHA : DISABLED_ALPHA);
+            holder.mLabelView.setEnabled(enabledState);
+            holder.mViewItem.setEnabled(enabledState);
             holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
             holder.mViewItem.setVisibility(View.VISIBLE);
             holder.mSwitchItem.setVisibility(View.GONE);
@@ -463,12 +474,15 @@
 
         private void updateInvisibleActionItemVisibility(@NonNull Context context,
                 @NonNull ViewHolder holder) {
-            final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
-
+            holder.mIconView.setColorFilter(null);
+            holder.mIconView.setAlpha(ENABLED_ALPHA);
+            holder.mLabelView.setEnabled(true);
+            holder.mViewItem.setEnabled(true);
             holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
             holder.mViewItem.setVisibility(View.VISIBLE);
             holder.mSwitchItem.setVisibility(View.GONE);
-            holder.mItemContainer.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+            holder.mItemContainer.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
+                    ? View.VISIBLE : View.GONE);
         }
 
         private void updateIntuitiveActionItemVisibility(@NonNull Context context,
@@ -478,6 +492,10 @@
                     ? isWhiteListingServiceEnabled(context, target)
                     : isAccessibilityServiceEnabled(context, target);
 
+            holder.mIconView.setColorFilter(null);
+            holder.mIconView.setAlpha(ENABLED_ALPHA);
+            holder.mLabelView.setEnabled(true);
+            holder.mViewItem.setEnabled(true);
             holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
             holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
             holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
@@ -487,12 +505,13 @@
 
         private void updateBounceActionItemVisibility(@NonNull Context context,
                 @NonNull ViewHolder holder) {
-            final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
-
-            holder.mViewItem.setImageDrawable(
-                    isEditMenuMode ? context.getDrawable(R.drawable.ic_delete_item)
-                            : context.getDrawable(R.drawable.ic_open_in_new));
-            holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+            holder.mIconView.setColorFilter(null);
+            holder.mIconView.setAlpha(ENABLED_ALPHA);
+            holder.mLabelView.setEnabled(true);
+            holder.mViewItem.setEnabled(true);
+            holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+            holder.mViewItem.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
+                    ? View.VISIBLE : View.GONE);
             holder.mSwitchItem.setVisibility(View.GONE);
             holder.mItemContainer.setVisibility(View.VISIBLE);
         }
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index b232efc..3322834 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -253,6 +253,13 @@
     public static final String NOTIFICATIONS_USE_PEOPLE_FILTERING =
             "notifications_use_people_filtering";
 
+    /**
+     * (boolean) Whether or not to enable user dismissing of foreground service notifications
+     * into a new section at the bottom of the notification shade.
+     */
+    public static final String NOTIFICATIONS_ALLOW_FGS_DISMISSAL =
+            "notifications_allow_fgs_dismissal";
+
     // Flags related to brightline falsing
 
     /**
diff --git a/core/java/com/android/internal/util/ObjectUtils.java b/core/java/com/android/internal/util/ObjectUtils.java
index 5568d91..0e7b93d 100644
--- a/core/java/com/android/internal/util/ObjectUtils.java
+++ b/core/java/com/android/internal/util/ObjectUtils.java
@@ -46,4 +46,12 @@
             return (b != null) ? -1 : 0;
         }
     }
+
+    /**
+     * Returns its first argument if non-null, and the second otherwise.
+     */
+    @Nullable
+    public static <T> T getOrElse(@Nullable final T object, @Nullable final T otherwise) {
+        return null != object ? object : otherwise;
+    }
 }
diff --git a/core/proto/android/server/connectivity/data_stall_event.proto b/core/proto/android/server/connectivity/data_stall_event.proto
index a82326f..23fcf6e 100644
--- a/core/proto/android/server/connectivity/data_stall_event.proto
+++ b/core/proto/android/server/connectivity/data_stall_event.proto
@@ -34,7 +34,7 @@
     AP_BAND_5GHZ = 2;
 }
 
-// Refer to definition in ServiceState.java.
+// Refer to definition in TelephonyManager.java.
 enum RadioTech {
   RADIO_TECHNOLOGY_UNKNOWN = 0;
   RADIO_TECHNOLOGY_GPRS = 1;
@@ -49,8 +49,8 @@
   RADIO_TECHNOLOGY_HSUPA = 10;
   RADIO_TECHNOLOGY_HSPA = 11;
   RADIO_TECHNOLOGY_EVDO_B = 12;
-  RADIO_TECHNOLOGY_EHRPD = 13;
-  RADIO_TECHNOLOGY_LTE = 14;
+  RADIO_TECHNOLOGY_LTE = 13;
+  RADIO_TECHNOLOGY_EHRPD = 14;
   RADIO_TECHNOLOGY_HSPAP = 15;
   RADIO_TECHNOLOGY_GSM = 16;
   RADIO_TECHNOLOGY_TD_SCDMA = 17;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6df0161..8b8b09f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -377,7 +377,7 @@
     <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION" />
     <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW" />
     <protected-broadcast android:name="android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION" />
-    <protected-broadcast android:name="android.net.wifi.action.WIFI_SCAN_AVAILABLE" />
+    <protected-broadcast android:name="android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
     <protected-broadcast android:name="android.net.wifi.supplicant.STATE_CHANGE" />
     <protected-broadcast android:name="android.net.wifi.p2p.STATE_CHANGED" />
@@ -2088,7 +2088,6 @@
          Allows reading of detailed information about phone state for special-use applications
          such as dialers, carrier applications, or ims applications. -->
     <permission android:name="android.permission.READ_PRECISE_PHONE_STATE"
-        android:permissionGroup="android.permission-group.UNDEFINED"
         android:protectionLevel="signature|privileged" />
 
     <!-- @SystemApi Allows read access to privileged phone state.
@@ -4018,9 +4017,9 @@
          statistics
          <p>Declaring the permission implies intention to use the API and the user of the
          device can grant permission through the Settings application.
-         <p>Protection level: signature|privileged|development|appop -->
+         <p>Protection level: signature|privileged|development|appop|retailDemo -->
     <permission android:name="android.permission.PACKAGE_USAGE_STATS"
-        android:protectionLevel="signature|privileged|development|appop" />
+        android:protectionLevel="signature|privileged|development|appop|retailDemo" />
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
 
     <!-- @hide @SystemApi Allows an application to observe usage time of apps. The app can register
diff --git a/core/res/res/drawable/ic_open_in_new.xml b/core/res/res/drawable/ic_open_in_new.xml
deleted file mode 100644
index 67378c8..0000000
--- a/core/res/res/drawable/ic_open_in_new.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-    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.
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24"
-    android:tint="?attr/colorControlNormal">
-  <path
-      android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"
-      android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/core/res/res/values-mcc334/config.xml b/core/res/res/values-mcc334/config.xml
new file mode 100644
index 0000000..e99c9a0
--- /dev/null
+++ b/core/res/res/values-mcc334/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc732/config.xml b/core/res/res/values-mcc732/config.xml
new file mode 100644
index 0000000..e99c9a0
--- /dev/null
+++ b/core/res/res/values-mcc732/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc740/config.xml b/core/res/res/values-mcc740/config.xml
new file mode 100644
index 0000000..e99c9a0
--- /dev/null
+++ b/core/res/res/values-mcc740/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 059bc44..b22e186 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -301,6 +301,9 @@
         <!-- Additional flag from base permission type: this permission can be automatically
             granted to the system companion device manager service -->
         <flag name="companion" value="0x800000" />
+        <!-- Additional flag from base permission type: this permission will be granted to the
+             retail demo app, as defined by the OEM. -->
+        <flag name="retailDemo" value="0x1000000" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1c9cccb..3d7b1e1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3594,7 +3594,6 @@
      -->
     <string name="config_defaultWellbeingPackage" translatable="false"></string>
 
-
     <!-- The package name for the system telephony apps.
          This package must be trusted, as it will be granted with permissions with special telephony
          protection level. Note, framework by default support multiple telephony apps, each package
@@ -3662,6 +3661,19 @@
     -->
     <string name="config_defaultContentSuggestionsService" translatable="false"></string>
 
+    <!-- The package name for the default retail demo app.
+         This package must be trusted, as it has the permissions to query the usage stats on the
+         device.
+         Example: "com.google.android.retaildemo"
+     -->
+    <string name="config_retailDemoPackage" translatable="false"></string>
+
+    <!-- The package signature hash for the default retail demo app.
+         This package must be trusted, as it has the permissions to query the usage stats on the
+         device.
+     -->
+    <string name="config_retailDemoPackageSignature" translatable="false"></string>
+
     <!-- Whether the device uses the default focus highlight when focus state isn't specified. -->
     <bool name="config_useDefaultFocusHighlight">true</bool>
 
@@ -4335,4 +4347,9 @@
     <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
          check after reboot or airplane mode toggling -->
     <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">false</bool>
+
+    <!-- Screen Wake Keys
+         Determines whether the specified key groups can be used to wake up the device. -->
+    <bool name="config_wakeOnDpadKeyPress">true</bool>
+    <bool name="config_wakeOnAssistKeyPress">true</bool>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 0b5082c..22abedc 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -33,9 +33,11 @@
     <dimen name="toast_y_offset">24dp</dimen>
     <!-- Height of the status bar -->
     <dimen name="status_bar_height">@dimen/status_bar_height_portrait</dimen>
-    <!-- Height of the status bar in portrait -->
+    <!-- Height of the status bar in portrait. The height should be
+         Max((status bar content height + waterfall top size), top cutout size) -->
     <dimen name="status_bar_height_portrait">24dp</dimen>
-    <!-- Height of the status bar in landscape -->
+    <!-- Height of the status bar in landscape. The height should be
+         Max((status bar content height + waterfall top size), top cutout size) -->
     <dimen name="status_bar_height_landscape">@dimen/status_bar_height_portrait</dimen>
     <!-- Height of area above QQS where battery/time go -->
     <dimen name="quick_qs_offset_height">48dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8f56164..36296a8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3028,6 +3028,10 @@
   <java-symbol type="id" name="notification_action_list_margin_target" />
   <java-symbol type="dimen" name="notification_action_disabled_alpha" />
 
+  <!-- Override Wake Key Behavior When Screen is Off -->
+  <java-symbol type="bool" name="config_wakeOnDpadKeyPress" />
+  <java-symbol type="bool" name="config_wakeOnAssistKeyPress" />
+
   <!-- Pinner Service -->
   <java-symbol type="array" name="config_defaultPinnerServiceFiles" />
   <java-symbol type="bool" name="config_pinnerCameraApp" />
@@ -3227,7 +3231,6 @@
   <java-symbol type="drawable" name="ic_accessibility_magnification" />
 
   <java-symbol type="drawable" name="ic_delete_item" />
-  <java-symbol type="drawable" name="ic_open_in_new" />
 
   <!-- com.android.internal.widget.RecyclerView -->
   <java-symbol type="id" name="item_touch_helper_previous_elevation"/>
@@ -3397,6 +3400,8 @@
   <java-symbol type="string" name="config_defaultAttentionService" />
   <java-symbol type="string" name="config_defaultSystemCaptionsService" />
   <java-symbol type="string" name="config_defaultSystemCaptionsManagerService" />
+  <java-symbol type="string" name="config_retailDemoPackage" />
+  <java-symbol type="string" name="config_retailDemoPackageSignature" />
 
   <java-symbol type="string" name="notification_channel_foreground_service" />
   <java-symbol type="string" name="foreground_service_app_in_background" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 692af58..b5eba09 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -132,6 +132,7 @@
         <permission name="android.permission.APPROVE_INCIDENT_REPORTS"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+        <permission name="android.permission.PACKAGE_USAGE_STATS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.phone">
@@ -400,5 +401,6 @@
     </privapp-permissions>
     <privapp-permissions package="com.android.settings">
         <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
+        <permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
     </privapp-permissions>
 </permissions>
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index b940cff..6f4af3d 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -24,6 +24,7 @@
 #include <SkOverdrawColorFilter.h>
 #include <SkPicture.h>
 #include <SkPictureRecorder.h>
+#include <SkTypeface.h>
 #include <SkSerialProcs.h>
 #include "LightingInfo.h"
 #include "VectorDrawable.h"
@@ -264,6 +265,9 @@
         SkSerialProcs procs;
         procs.fImageProc = SkSharingSerialContext::serializeImage;
         procs.fImageCtx = mSerialContext.get();
+        procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){
+            return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
+        };
         // SkDocuments don't take owership of the streams they write.
         // we need to keep it until after mMultiPic.close()
         // procs is passed as a pointer, but just as a method of having an optional default.
@@ -405,6 +409,10 @@
                 std::invoke(mPictureCapturedCallback, std::move(picture));
             } else {
                 // single frame skp to file
+                SkSerialProcs procs;
+                procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){
+                    return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
+                };
                 auto data = picture->serialize();
                 savePictureAsync(data, mCapturedFile);
                 mCaptureSequence = 0;
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 5ef466d..69be8b3 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -61,6 +61,7 @@
  * @see MediaSession
  * @see MediaController
  */
+// TODO: (jinpark) Add API for getting and setting session policies from MediaSessionService.
 @SystemService(Context.MEDIA_SESSION_SERVICE)
 public final class MediaSessionManager {
     private static final String TAG = "SessionManager";
diff --git a/media/tests/MediaRouteProvider/Android.bp b/media/tests/MediaRouteProvider/Android.bp
deleted file mode 100644
index da42824..0000000
--- a/media/tests/MediaRouteProvider/Android.bp
+++ /dev/null
@@ -1,18 +0,0 @@
-android_test {
-    name: "mediarouteprovider",
-
-    srcs: ["**/*.java"],
-
-    libs: [
-        "android.test.runner",
-        "android.test.base",
-    ],
-
-    static_libs: [
-        "android-support-test",
-        "mockito-target-minus-junit4",
-    ],
-
-    platform_apis: true,
-    certificate: "platform",
-}
\ No newline at end of file
diff --git a/media/tests/MediaRouteProvider/AndroidManifest.xml b/media/tests/MediaRouteProvider/AndroidManifest.xml
deleted file mode 100644
index 489a621..0000000
--- a/media/tests/MediaRouteProvider/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.mediarouteprovider.example">
-
-    <application android:label="@string/app_name">
-        <uses-library android:name="android.test.runner" />
-        <service android:name=".SampleMediaRoute2ProviderService"
-            android:label="@string/app_name"
-            android:exported="true">
-            <intent-filter>
-                <action android:name="android.media.MediaRoute2ProviderService" />
-            </intent-filter>
-       </service>
-    </application>
-</manifest>
diff --git a/media/tests/MediaRouteProvider/res/values/strings.xml b/media/tests/MediaRouteProvider/res/values/strings.xml
deleted file mode 100644
index bb97064..0000000
--- a/media/tests/MediaRouteProvider/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <!-- name of the app [CHAR LIMIT=25]-->
-    <string name="app_name">SampleMediaRouteProvider</string>
-</resources>
\ No newline at end of file
diff --git a/media/tests/MediaRouter/AndroidManifest.xml b/media/tests/MediaRouter/AndroidManifest.xml
index a34a264..9546500 100644
--- a/media/tests/MediaRouter/AndroidManifest.xml
+++ b/media/tests/MediaRouter/AndroidManifest.xml
@@ -17,10 +17,14 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.mediaroutertest">
 
-    <uses-permission android:name="android.permission.CONTROL_MEDIA_ROUTE" />
-
     <application android:label="@string/app_name">
         <uses-library android:name="android.test.runner" />
+        <service android:name=".SampleMediaRoute2ProviderService"
+                 android:exported="true">
+            <intent-filter>
+                <action android:name="android.media.MediaRoute2ProviderService" />
+            </intent-filter>
+        </service>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 16259ab..4316e42 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -16,9 +16,23 @@
 
 package com.android.mediaroutertest;
 
+import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
 import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
 import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
 
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ACTION_REMOVE_ROUTE;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.FEATURE_SAMPLE;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.FEATURE_SPECIAL;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID1;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID2;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID_FIXED_VOLUME;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID_VARIABLE_VOLUME;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_NAME1;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_NAME2;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.VOLUME_MAX;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -56,50 +70,6 @@
 @SmallTest
 public class MediaRouterManagerTest {
     private static final String TAG = "MediaRouterManagerTest";
-
-    public static final String SAMPLE_PROVIDER_ROUTES_ID_PREFIX =
-            "com.android.mediarouteprovider.example/.SampleMediaRoute2ProviderService:";
-
-    // Must be the same as SampleMediaRoute2ProviderService except the prefix of IDs.
-    public static final String ROUTE_ID1 = SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id1";
-    public static final String ROUTE_NAME1 = "Sample Route 1";
-    public static final String ROUTE_ID2 = SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id2";
-    public static final String ROUTE_NAME2 = "Sample Route 2";
-    public static final String ROUTE_ID3_SESSION_CREATION_FAILED =
-            SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id3_session_creation_failed";
-    public static final String ROUTE_NAME3 = "Sample Route 3 - Session creation failed";
-    public static final String ROUTE_ID4_TO_SELECT_AND_DESELECT =
-            SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id4_to_select_and_deselect";
-    public static final String ROUTE_NAME4 = "Sample Route 4 - Route to select and deselect";
-    public static final String ROUTE_ID5_TO_TRANSFER_TO =
-            SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id5_to_transfer_to";
-    public static final String ROUTE_NAME5 = "Sample Route 5 - Route to transfer to";
-
-    public static final String ROUTE_ID_SPECIAL_FEATURE =
-            SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_special_feature";
-    public static final String ROUTE_NAME_SPECIAL_FEATURE = "Special Feature Route";
-
-    public static final String SYSTEM_PROVIDER_ID =
-            "com.android.server.media/.SystemMediaRoute2Provider";
-
-    public static final int VOLUME_MAX = 100;
-    public static final String ROUTE_ID_FIXED_VOLUME =
-            SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_fixed_volume";
-    public static final String ROUTE_NAME_FIXED_VOLUME = "Fixed Volume Route";
-    public static final String ROUTE_ID_VARIABLE_VOLUME =
-            SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_variable_volume";
-    public static final String ROUTE_NAME_VARIABLE_VOLUME = "Variable Volume Route";
-
-    public static final String ACTION_REMOVE_ROUTE =
-            "com.android.mediarouteprovider.action_remove_route";
-
-    public static final String FEATURE_SAMPLE =
-            "com.android.mediarouteprovider.FEATURE_SAMPLE";
-    public static final String FEATURE_SPECIAL =
-            "com.android.mediarouteprovider.FEATURE_SPECIAL";
-
-    private static final String FEATURE_LIVE_AUDIO = "android.media.intent.route.LIVE_AUDIO";
-
     private static final int TIMEOUT_MS = 5000;
 
     private Context mContext;
@@ -155,7 +125,8 @@
             public void onRoutesAdded(List<MediaRoute2Info> routes) {
                 assertTrue(routes.size() > 0);
                 for (MediaRoute2Info route : routes) {
-                    if (route.getId().equals(ROUTE_ID1) && route.getName().equals(ROUTE_NAME1)) {
+                    if (route.getOriginalId().equals(ROUTE_ID1)
+                            && route.getName().equals(ROUTE_NAME1)) {
                         latch.countDown();
                     }
                 }
@@ -176,7 +147,8 @@
             public void onRoutesRemoved(List<MediaRoute2Info> routes) {
                 assertTrue(routes.size() > 0);
                 for (MediaRoute2Info route : routes) {
-                    if (route.getId().equals(ROUTE_ID2) && route.getName().equals(ROUTE_NAME2)) {
+                    if (route.getOriginalId().equals(ROUTE_ID2)
+                            && route.getName().equals(ROUTE_NAME2)) {
                         latch.countDown();
                     }
                 }
@@ -352,8 +324,7 @@
             @Override
             public void onRoutesAdded(List<MediaRoute2Info> routes) {
                 for (int i = 0; i < routes.size(); i++) {
-                    //TODO: use isSystem() or similar method when it's ready
-                    if (!TextUtils.equals(routes.get(i).getProviderId(), SYSTEM_PROVIDER_ID)) {
+                    if (!routes.get(i).isSystemRoute()) {
                         latch.countDown();
                         break;
                     }
@@ -406,7 +377,7 @@
     static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
         Map<String, MediaRoute2Info> routeMap = new HashMap<>();
         for (MediaRoute2Info route : routes) {
-            routeMap.put(route.getId(), route);
+            routeMap.put(route.getOriginalId(), route);
         }
         return routeMap;
     }
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
similarity index 99%
rename from media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
rename to media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
index 6595cae..e29b323 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.mediarouteprovider.example;
+package com.android.mediaroutertest;
 
 import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
 import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index a784e04..ddb7341 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.bluetooth;
 
+import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO;
 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
 
@@ -45,6 +46,7 @@
     private boolean mIsProfileReady;
 
     private final CachedBluetoothDeviceManager mDeviceManager;
+    private final BluetoothAdapter mBluetoothAdapter;
 
     static final ParcelUuid[] SINK_UUIDS = {
         BluetoothUuid.A2DP_SINK,
@@ -99,7 +101,8 @@
         mContext = context;
         mDeviceManager = deviceManager;
         mProfileManager = profileManager;
-        BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new A2dpServiceListener(),
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        mBluetoothAdapter.getProfileProxy(context, new A2dpServiceListener(),
                 BluetoothProfile.A2DP);
     }
 
@@ -173,8 +176,10 @@
     }
 
     public boolean setActiveDevice(BluetoothDevice device) {
-        if (mService == null) return false;
-        return mService.setActiveDevice(device);
+        if (mBluetoothAdapter == null) {
+            return false;
+        }
+        return mBluetoothAdapter.setActiveDevice(device, ACTIVE_DEVICE_AUDIO);
     }
 
     public BluetoothDevice getActiveDevice() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index d65b5da..218d0b2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.bluetooth;
 
+import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL;
 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
 
@@ -45,6 +46,7 @@
 
     private final CachedBluetoothDeviceManager mDeviceManager;
     private final LocalBluetoothProfileManager mProfileManager;
+    private final BluetoothAdapter mBluetoothAdapter;
 
     static final ParcelUuid[] UUIDS = {
         BluetoothUuid.HSP,
@@ -99,7 +101,8 @@
             LocalBluetoothProfileManager profileManager) {
         mDeviceManager = deviceManager;
         mProfileManager = profileManager;
-        BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new HeadsetServiceListener(),
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        mBluetoothAdapter.getProfileProxy(context, new HeadsetServiceListener(),
                 BluetoothProfile.HEADSET);
     }
 
@@ -134,10 +137,10 @@
     }
 
     public boolean setActiveDevice(BluetoothDevice device) {
-        if (mService == null) {
+        if (mBluetoothAdapter == null) {
             return false;
         }
-        return mService.setActiveDevice(device);
+        return mBluetoothAdapter.setActiveDevice(device, ACTIVE_DEVICE_PHONE_CALL);
     }
 
     public BluetoothDevice getActiveDevice() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 9f1af66..b82fb37 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.bluetooth;
 
+import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_ALL;
 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
 
@@ -45,6 +46,7 @@
 
     static final String NAME = "HearingAid";
     private final LocalBluetoothProfileManager mProfileManager;
+    private final BluetoothAdapter mBluetoothAdapter;
 
     // Order of this profile in device profiles list
     private static final int ORDINAL = 1;
@@ -97,7 +99,8 @@
         mContext = context;
         mDeviceManager = deviceManager;
         mProfileManager = profileManager;
-        BluetoothAdapter.getDefaultAdapter().getProfileProxy(context,
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        mBluetoothAdapter.getProfileProxy(context,
                 new HearingAidServiceListener(), BluetoothProfile.HEARING_AID);
     }
 
@@ -171,8 +174,10 @@
     }
 
     public boolean setActiveDevice(BluetoothDevice device) {
-        if (mService == null) return false;
-        return mService.setActiveDevice(device);
+        if (mBluetoothAdapter == null) {
+            return false;
+        }
+        return mBluetoothAdapter.setActiveDevice(device, ACTIVE_DEVICE_ALL);
     }
 
     public List<BluetoothDevice> getActiveDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
index 9db4a35..b4c95e6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
@@ -35,8 +35,10 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.zip.GZIPInputStream;
 
 /**
@@ -84,7 +86,7 @@
      * "9645f39e9db895a4aa6e02cb57294595". Here "9645f39e9db895a4aa6e02cb57294595" is a MD5 sum
      * of the content of packages/services/Telephony/MODULE_LICENSE_APACHE2.
      */
-    private final Map<String, String> mFileNameToContentIdMap = new HashMap();
+    private final Map<String, Set<String>> mFileNameToContentIdMap = new HashMap();
 
     /*
      * A map from a content id (MD5 sum of file content) to a license file content.
@@ -186,10 +188,10 @@
      *     </licenses>
      */
     @VisibleForTesting
-    static void parse(InputStreamReader in, Map<String, String> outFileNameToContentIdMap,
+    static void parse(InputStreamReader in, Map<String, Set<String>> outFileNameToContentIdMap,
             Map<String, String> outContentIdToFileContentMap)
                     throws XmlPullParserException, IOException {
-        Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
+        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<String, Set<String>>();
         Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
 
         XmlPullParser parser = Xml.newPullParser();
@@ -206,7 +208,10 @@
                     if (!TextUtils.isEmpty(contentId)) {
                         String fileName = readText(parser).trim();
                         if (!TextUtils.isEmpty(fileName)) {
-                            fileNameToContentIdMap.put(fileName, contentId);
+                            Set<String> contentIds =
+                                    fileNameToContentIdMap.computeIfAbsent(
+                                            fileName, k -> new HashSet<>());
+                            contentIds.add(contentId);
                         }
                     }
                 } else if (TAG_FILE_CONTENT.equals(parser.getName())) {
@@ -224,7 +229,13 @@
 
             state = parser.next();
         }
-        outFileNameToContentIdMap.putAll(fileNameToContentIdMap);
+        for (Map.Entry<String, Set<String>> entry : fileNameToContentIdMap.entrySet()) {
+            outFileNameToContentIdMap.merge(
+                    entry.getKey(), entry.getValue(), (s1, s2) -> {
+                        s1.addAll(s2);
+                        return s1;
+                    });
+        }
         outContentIdToFileContentMap.putAll(contentIdToFileContentMap);
     }
 
@@ -240,7 +251,7 @@
     }
 
     @VisibleForTesting
-    static void generateHtml(Map<String, String> fileNameToContentIdMap,
+    static void generateHtml(Map<String, Set<String>> fileNameToContentIdMap,
             Map<String, String> contentIdToFileContentMap, PrintWriter writer,
             String noticeHeader) {
         List<String> fileNameList = new ArrayList();
@@ -259,19 +270,20 @@
 
         // Prints all the file list with a link to its license file content.
         for (String fileName : fileNameList) {
-            String contentId = fileNameToContentIdMap.get(fileName);
-            // Assigns an id to a newly referred license file content.
-            if (!contentIdToOrderMap.containsKey(contentId)) {
-                contentIdToOrderMap.put(contentId, count);
+            for (String contentId : fileNameToContentIdMap.get(fileName)) {
+                // Assigns an id to a newly referred license file content.
+                if (!contentIdToOrderMap.containsKey(contentId)) {
+                    contentIdToOrderMap.put(contentId, count);
 
-                // An index in contentIdAndFileNamesList is the order of each element.
-                contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
-                count++;
+                    // An index in contentIdAndFileNamesList is the order of each element.
+                    contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
+                    count++;
+                }
+
+                int id = contentIdToOrderMap.get(contentId);
+                contentIdAndFileNamesList.get(id).mFileNameList.add(fileName);
+                writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
             }
-
-            int id = contentIdToOrderMap.get(contentId);
-            contentIdAndFileNamesList.get(id).mFileNameList.add(fileName);
-            writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
         }
 
         writer.println(HTML_MIDDLE_STRING);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index 4b5e909..e87461f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -28,8 +28,11 @@
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 @RunWith(RobolectricTestRunner.class)
 public class LicenseHtmlGeneratorFromXmlTest {
@@ -68,6 +71,7 @@
 
     private static final String HTML_BODY_STRING =
             "<li><a href=\"#id0\">/file0</a></li>\n"
+            + "<li><a href=\"#id1\">/file0</a></li>\n"
             + "<li><a href=\"#id0\">/file1</a></li>\n"
             + "</ul>\n"
             + "</div><!-- table of contents -->\n"
@@ -82,6 +86,15 @@
             + "license content #0\n"
             + "</pre><!-- license-text -->\n"
             + "</td></tr><!-- same-license -->\n"
+            + "<tr id=\"id1\"><td class=\"same-license\">\n"
+            + "<div class=\"label\">Notices for file(s):</div>\n"
+            + "<div class=\"file-list\">\n"
+            + "/file0 <br/>\n"
+            + "</div><!-- file-list -->\n"
+            + "<pre class=\"license-text\">\n"
+            + "license content #1\n"
+            + "</pre><!-- license-text -->\n"
+            + "</td></tr><!-- same-license -->\n"
             + "</table></body></html>\n";
 
     private static final String EXPECTED_HTML_STRING = HTML_HEAD_STRING + HTML_BODY_STRING;
@@ -91,22 +104,22 @@
 
     @Test
     public void testParseValidXmlStream() throws XmlPullParserException, IOException {
-        Map<String, String> fileNameToContentIdMap = new HashMap<>();
+        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
         Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
         LicenseHtmlGeneratorFromXml.parse(
                 new InputStreamReader(new ByteArrayInputStream(VALILD_XML_STRING.getBytes())),
                 fileNameToContentIdMap, contentIdToFileContentMap);
         assertThat(fileNameToContentIdMap.size()).isEqualTo(2);
-        assertThat(fileNameToContentIdMap.get("/file0")).isEqualTo("0");
-        assertThat(fileNameToContentIdMap.get("/file1")).isEqualTo("0");
+        assertThat(fileNameToContentIdMap.get("/file0")).containsExactly("0");
+        assertThat(fileNameToContentIdMap.get("/file1")).containsExactly("0");
         assertThat(contentIdToFileContentMap.size()).isEqualTo(1);
         assertThat(contentIdToFileContentMap.get("0")).isEqualTo("license content #0");
     }
 
     @Test(expected = XmlPullParserException.class)
     public void testParseInvalidXmlStream() throws XmlPullParserException, IOException {
-        Map<String, String> fileNameToContentIdMap = new HashMap<>();
+        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
         Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
         LicenseHtmlGeneratorFromXml.parse(
@@ -116,12 +129,13 @@
 
     @Test
     public void testGenerateHtml() {
-        Map<String, String> fileNameToContentIdMap = new HashMap<>();
+        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
         Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
-        fileNameToContentIdMap.put("/file0", "0");
-        fileNameToContentIdMap.put("/file1", "0");
+        fileNameToContentIdMap.put("/file0", new HashSet<String>(Arrays.asList("0", "1")));
+        fileNameToContentIdMap.put("/file1", new HashSet<String>(Arrays.asList("0")));
         contentIdToFileContentMap.put("0", "license content #0");
+        contentIdToFileContentMap.put("1", "license content #1");
 
         StringWriter output = new StringWriter();
         LicenseHtmlGeneratorFromXml.generateHtml(
@@ -131,12 +145,13 @@
 
     @Test
     public void testGenerateHtmlWithCustomHeading() {
-        Map<String, String> fileNameToContentIdMap = new HashMap<>();
+        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
         Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
-        fileNameToContentIdMap.put("/file0", "0");
-        fileNameToContentIdMap.put("/file1", "0");
+        fileNameToContentIdMap.put("/file0", new HashSet<String>(Arrays.asList("0", "1")));
+        fileNameToContentIdMap.put("/file1", new HashSet<String>(Arrays.asList("0")));
         contentIdToFileContentMap.put("0", "license content #0");
+        contentIdToFileContentMap.put("1", "license content #1");
 
         StringWriter output = new StringWriter();
         LicenseHtmlGeneratorFromXml.generateHtml(
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 149eaf4..139a8c3 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -644,7 +644,7 @@
 
         <activity android:name=".controls.management.ControlsProviderSelectorActivity"
                   android:label="Controls Providers"
-                  android:theme="@style/Theme.SystemUI"
+                  android:theme="@style/Theme.ControlsManagement"
                   android:exported="true"
                   android:showForAllUsers="true"
                   android:excludeFromRecents="true"
@@ -654,7 +654,7 @@
 
         <activity android:name=".controls.management.ControlsFavoritingActivity"
                   android:parentActivityName=".controls.management.ControlsProviderSelectorActivity"
-                  android:theme="@style/Theme.SystemUI"
+                  android:theme="@style/Theme.ControlsManagement"
                   android:excludeFromRecents="true"
                   android:showForAllUsers="true"
                   android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 6518924..01811e9 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -14,9 +14,6 @@
 
 package com.android.systemui.plugins.qs;
 
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.IntDef;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.metrics.LogMaker;
@@ -28,7 +25,6 @@
 import com.android.systemui.plugins.qs.QSTile.Icon;
 import com.android.systemui.plugins.qs.QSTile.State;
 
-import java.lang.annotation.Retention;
 import java.util.Objects;
 import java.util.function.Supplier;
 
@@ -84,17 +80,6 @@
         return logMaker;
     }
 
-    @Retention(SOURCE)
-    @IntDef({COLOR_TILE_ACCENT, COLOR_TILE_RED, COLOR_TILE_BLUE, COLOR_TILE_YELLOW,
-            COLOR_TILE_GREEN})
-    @interface ColorTile {}
-    int COLOR_TILE_ACCENT = 0;
-    int COLOR_TILE_RED = 1;
-    int COLOR_TILE_BLUE = 2;
-    int COLOR_TILE_YELLOW = 3;
-    int COLOR_TILE_GREEN = 4;
-    default void setColor(@ColorTile int color) {}
-
     @ProvidesInterface(version = Callback.VERSION)
     public interface Callback {
         public static final int VERSION = 1;
@@ -141,7 +126,6 @@
         public SlashState slash;
         public boolean handlesLongClick = true;
         public boolean showRippleEffect = true;
-        public int colorActive = -1;
 
         public boolean copyTo(State other) {
             if (other == null) throw new IllegalArgumentException();
@@ -161,8 +145,7 @@
                     || !Objects.equals(other.dualTarget, dualTarget)
                     || !Objects.equals(other.slash, slash)
                     || !Objects.equals(other.handlesLongClick, handlesLongClick)
-                    || !Objects.equals(other.showRippleEffect, showRippleEffect)
-                    || !Objects.equals(other.colorActive, colorActive);
+                    || !Objects.equals(other.showRippleEffect, showRippleEffect);
             other.icon = icon;
             other.iconSupplier = iconSupplier;
             other.label = label;
@@ -177,7 +160,6 @@
             other.slash = slash != null ? slash.copy() : null;
             other.handlesLongClick = handlesLongClick;
             other.showRippleEffect = showRippleEffect;
-            other.colorActive = colorActive;
             return changed;
         }
 
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 3e74970..2a2ba1b 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -16,6 +16,7 @@
 -keep class com.android.systemui.statusbar.tv.TvStatusBar
 -keep class com.android.systemui.car.CarSystemUIFactory
 -keep class com.android.systemui.SystemUIFactory
+-keep class com.android.systemui.tv.TvSystemUIFactory
 -keep class * extends com.android.systemui.SystemUI
 -keep class * implements com.android.systemui.SystemUI$Injector
 
diff --git a/packages/SystemUI/res-keyguard/layout/controls_management.xml b/packages/SystemUI/res-keyguard/layout/controls_management.xml
new file mode 100644
index 0000000..8330258
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/controls_management.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_horizontal"
+    android:paddingTop="@dimen/controls_management_top_padding"
+    android:paddingStart="@dimen/controls_management_side_padding"
+    android:paddingEnd="@dimen/controls_management_side_padding" >
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:textSize="@dimen/controls_title_size"
+        android:textAlignment="center" />
+
+    <TextView
+        android:id="@+id/subtitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/controls_management_titles_margin"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textAlignment="center" />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/controls_management_list_margin" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml b/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml
new file mode 100644
index 0000000..c547c52
--- /dev/null
+++ b/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM14,6h-4L10,4h4v2z"
+        android:fillColor="?android:attr/colorAccent"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml b/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml
new file mode 100644
index 0000000..e456e29
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <gradient
+        android:angle="90"
+        android:startColor="#ff000000"
+        android:endColor="#00000000"
+        android:type="linear" />
+</shape>
diff --git a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
index c3fa39e..c40e47d 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
@@ -34,20 +34,15 @@
             android:layout_weight="1"/>
 
         <ImageView
-            android:layout_width="32dp"
-            android:layout_height="32dp"
-            android:background="@drawable/auth_dialog_lock"/>
+            android:id="@+id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
 
         <TextView
             android:id="@+id/title"
-            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginHorizontal="24dp"
-            android:layout_marginTop="12dp"
-            android:textSize="20sp"
-            android:gravity="center"
-            android:textColor="?android:attr/textColorPrimary"/>
+            style="@style/TextAppearance.AuthCredential.Title"/>
 
         <TextView
             android:id="@+id/subtitle"
@@ -63,17 +58,41 @@
             android:id="@+id/description"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginHorizontal="24dp"
-            android:layout_marginTop="8dp"
-            android:gravity="center"
-            android:textSize="16sp"
-            android:textColor="?android:attr/textColorPrimary"/>
+            style="@style/TextAppearance.AuthCredential.Description"/>
 
         <Space
             android:layout_width="0dp"
             android:layout_height="0dp"
             android:layout_weight="1"/>
 
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:gravity="center"
+        android:paddingLeft="0dp"
+        android:paddingRight="0dp"
+        android:paddingTop="0dp"
+        android:paddingBottom="16dp"
+        android:clipToPadding="false">
+
+        <FrameLayout
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            style="@style/LockPatternContainerStyle">
+
+            <com.android.internal.widget.LockPatternView
+                android:id="@+id/lockPattern"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_gravity="center"
+                style="@style/LockPatternStyleBiometricPrompt"/>
+
+        </FrameLayout>
+
         <TextView
             android:id="@+id/error"
             android:layout_width="match_parent"
@@ -90,24 +109,4 @@
 
     </LinearLayout>
 
-    <LinearLayout
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:orientation="vertical"
-        android:gravity="center">
-
-        <com.android.internal.widget.LockPatternView
-            android:id="@+id/lockPattern"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="40dp"
-            android:layout_marginRight="40dp"
-            android:layout_gravity="center"
-            android:clipChildren="false"
-            android:clipToPadding="false"
-            style="@style/LockPatternStyleBiometricPrompt"/>
-
-    </LinearLayout>
-
 </com.android.systemui.biometrics.AuthCredentialPatternView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/app_item.xml b/packages/SystemUI/res/layout/app_item.xml
deleted file mode 100644
index 83e7887..0000000
--- a/packages/SystemUI/res/layout/app_item.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:background="?android:attr/selectableItemBackground"
-    android:gravity="center_vertical"
-    android:minHeight="?android:attr/listPreferredItemHeightSmall"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
-
-    <LinearLayout
-        android:id="@+id/icon_frame"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:gravity="start|center_vertical"
-        android:minWidth="56dp"
-        android:orientation="horizontal"
-        android:paddingEnd="8dp"
-        android:paddingTop="4dp"
-        android:paddingBottom="4dp">
-        <ImageView
-            android:id="@android:id/icon"
-            android:layout_width="@dimen/app_icon_size"
-            android:layout_height="@dimen/app_icon_size"/>
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:orientation="vertical"
-        android:paddingTop="16dp"
-        android:paddingBottom="16dp">
-
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal"
-            android:singleLine="true"
-            android:textAppearance="?android:attr/textAppearanceListItem"/>
-
-    </LinearLayout>
-
-    <LinearLayout
-        android:id="@android:id/widget_frame"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:gravity="center_vertical|end"
-        android:minWidth="64dp"
-        android:orientation="vertical"/>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 4aed033..a1c593f 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -28,20 +28,15 @@
         android:layout_weight="1"/>
 
     <ImageView
-        android:layout_width="32dp"
-        android:layout_height="32dp"
-        android:background="@drawable/auth_dialog_lock"/>
+        android:id="@+id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
 
     <TextView
         android:id="@+id/title"
-        android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginHorizontal="24dp"
-        android:layout_marginTop="12dp"
-        android:textSize="20sp"
-        android:gravity="center"
-        android:textColor="?android:attr/textColorPrimary"/>
+        style="@style/TextAppearance.AuthCredential.Title"/>
 
     <TextView
         android:id="@+id/subtitle"
@@ -57,11 +52,7 @@
         android:id="@+id/description"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginHorizontal="24dp"
-        android:layout_marginTop="8dp"
-        android:gravity="center"
-        android:textSize="16sp"
-        android:textColor="?android:attr/textColorPrimary"/>
+        style="@style/TextAppearance.AuthCredential.Description"/>
 
     <Space
         android:layout_width="0dp"
@@ -79,19 +70,14 @@
 
     <EditText
         android:id="@+id/lockPassword"
-        android:layout_marginBottom="20dp"
-        android:layout_marginLeft="100dp"
-        android:layout_marginRight="100dp"
         android:layout_width="208dp"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"
+        android:layout_gravity="center_horizontal"
         android:gravity="center"
         android:inputType="textPassword"
         android:maxLength="500"
-        android:textSize="16sp"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:imeOptions="flagForceAscii"
-        style="@style/LockPatternStyleBiometricPrompt"/>
+        android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+        style="@style/TextAppearance.AuthCredential.PasswordEntry"/>
 
     <Space
         android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
index c9edcd6..eda5ecb 100644
--- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
@@ -28,20 +28,15 @@
         android:layout_weight="1"/>
 
     <ImageView
-        android:layout_width="32dp"
-        android:layout_height="32dp"
-        android:background="@drawable/auth_dialog_lock"/>
+        android:id="@+id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
 
     <TextView
         android:id="@+id/title"
-        android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginHorizontal="24dp"
-        android:layout_marginTop="12dp"
-        android:textSize="20sp"
-        android:gravity="center"
-        android:textColor="?android:attr/textColorPrimary"/>
+        style="@style/TextAppearance.AuthCredential.Title"/>
 
     <TextView
         android:id="@+id/subtitle"
@@ -57,37 +52,49 @@
         android:id="@+id/description"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginHorizontal="24dp"
-        android:layout_marginTop="8dp"
-        android:gravity="center"
-        android:textSize="16sp"
-        android:textColor="?android:attr/textColorPrimary"/>
+        style="@style/TextAppearance.AuthCredential.Description"/>
 
     <Space
         android:layout_width="0dp"
         android:layout_height="0dp"
-        android:layout_weight="3"/>
+        android:layout_weight="1"/>
 
-    <TextView
-        android:id="@+id/error"
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginHorizontal="24dp"
-        android:textSize="16sp"
+        android:orientation="vertical"
         android:gravity="center"
-        android:textColor="?android:attr/colorError"/>
+        android:paddingLeft="0dp"
+        android:paddingRight="0dp"
+        android:paddingTop="0dp"
+        android:paddingBottom="16dp"
+        android:clipToPadding="false">
 
-    <com.android.internal.widget.LockPatternView
-        android:id="@+id/lockPattern"
-        android:layout_marginBottom="20dp"
-        android:layout_marginLeft="40dp"
-        android:layout_marginRight="40dp"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        style="@style/LockPatternStyleBiometricPrompt"/>
+        <FrameLayout
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            style="@style/LockPatternContainerStyle">
+
+            <com.android.internal.widget.LockPatternView
+                android:id="@+id/lockPattern"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_gravity="center"
+                style="@style/LockPatternStyleBiometricPrompt"/>
+
+        </FrameLayout>
+
+        <TextView
+            android:id="@+id/error"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="24dp"
+            android:textSize="16sp"
+            android:gravity="center"
+            android:textColor="?android:attr/colorError"/>
+
+    </LinearLayout>
 
     <Space
         android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/control_item.xml b/packages/SystemUI/res/layout/control_item.xml
deleted file mode 100644
index 85701aa..0000000
--- a/packages/SystemUI/res/layout/control_item.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  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.
--->
-<androidx.constraintlayout.widget.ConstraintLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="100dp"
-    android:padding="15dp"
-    android:clickable="true"
-    android:focusable="true">
-
-    <ImageView
-        android:id="@+id/icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-
-    <TextView
-        android:id="@+id/status"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="12sp"
-        android:textColor="?android:attr/textColorPrimary"
-        android:fontFamily="@*android:string/config_bodyFontFamily"
-        android:paddingLeft="3dp"
-        app:layout_constraintBottom_toBottomOf="@+id/icon"
-        app:layout_constraintStart_toEndOf="@+id/icon" />
-
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="18sp"
-        android:textColor="?android:attr/textColorPrimary"
-        android:fontFamily="@*android:string/config_headlineFontFamily"
-        app:layout_constraintBottom_toTopOf="@+id/subtitle"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@+id/icon" />
-
-    <TextView
-        android:id="@+id/subtitle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="16sp"
-        android:textColor="?android:attr/textColorSecondary"
-        android:fontFamily="@*android:string/config_headlineFontFamily"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toStartOf="parent" />
-
-    <CheckBox
-        android:id="@+id/favorite"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"/>
-</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/controls_app_item.xml b/packages/SystemUI/res/layout/controls_app_item.xml
new file mode 100644
index 0000000..d54cd6d
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_app_item.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="start|top"
+        android:gravity="center_vertical"
+        android:minHeight="?android:attr/listPreferredItemHeightSmall"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+        android:layout_marginBottom="@dimen/controls_app_bottom_margin">
+
+        <FrameLayout
+            android:id="@+id/icon_frame"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="start|center_vertical"
+            android:minWidth="56dp"
+            android:orientation="horizontal"
+            android:paddingTop="@dimen/controls_app_icon_frame_top_padding"
+            android:paddingBottom="@dimen/controls_app_icon_frame_top_padding"
+            android:paddingEnd="@dimen/controls_app_icon_frame_side_padding"
+            android:paddingStart="@dimen/controls_app_icon_frame_side_padding" >
+
+            <ImageView
+                android:id="@android:id/icon"
+                android:layout_width="@dimen/controls_app_icon_size"
+                android:layout_height="@dimen/controls_app_icon_size" />
+        </FrameLayout>
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:orientation="vertical"
+            android:paddingTop="@dimen/controls_app_text_padding"
+            android:paddingBottom="@dimen/controls_app_text_padding">
+
+            <TextView
+                android:id="@android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:ellipsize="end"
+                android:fadingEdge="horizontal"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:textColor="?android:attr/textColorPrimary"/>
+
+            <TextView
+                android:id="@+id/favorites"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:ellipsize="end"
+                android:fadingEdge="horizontal"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/textAppearanceSmall" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/controls_app_divider_height"
+        android:layout_gravity="center_horizontal|bottom"
+        android:layout_marginStart="@dimen/controls_app_divider_side_margin"
+        android:layout_marginEnd="@dimen/controls_app_divider_side_margin"
+        android:background="?android:attr/listDivider" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index cd7ec5e..68c8246 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -23,8 +23,8 @@
     android:padding="@dimen/control_padding"
     android:clickable="true"
     android:focusable="true"
-    android:layout_marginLeft="2dp"
-    android:layout_marginRight="2dp"
+    android:layout_marginLeft="@dimen/control_base_item_margin"
+    android:layout_marginRight="@dimen/control_base_item_margin"
     android:background="@drawable/control_background">
 
     <ImageView
@@ -38,10 +38,8 @@
         android:id="@+id/status"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textSize="@dimen/control_status_normal"
-        android:textColor="?android:attr/textColorPrimary"
-        android:fontFamily="@*android:string/config_bodyFontFamily"
-        android:paddingLeft="3dp"
+        android:textAppearance="@style/TextAppearance.Control.Status"
+        android:paddingStart="@dimen/control_status_padding"
         app:layout_constraintBottom_toBottomOf="@+id/icon"
         app:layout_constraintStart_toEndOf="@+id/icon" />
 
@@ -49,10 +47,8 @@
         android:id="@+id/status_extra"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textSize="@dimen/control_status_normal"
-        android:textColor="?android:attr/textColorPrimary"
-        android:fontFamily="@*android:string/config_bodyFontFamily"
-        android:paddingLeft="3dp"
+        android:textAppearance="@style/TextAppearance.Control.Status"
+        android:paddingStart="@dimen/control_status_padding"
         app:layout_constraintBottom_toBottomOf="@+id/icon"
         app:layout_constraintStart_toEndOf="@+id/status" />
 
@@ -60,9 +56,7 @@
         android:id="@+id/title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textSize="14sp"
-        android:textColor="?android:attr/textColorPrimary"
-        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textAppearance="@style/TextAppearance.Control.Title"
         app:layout_constraintBottom_toTopOf="@+id/subtitle"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@+id/icon" />
@@ -71,9 +65,15 @@
         android:id="@+id/subtitle"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textSize="12sp"
-        android:textColor="?android:attr/textColorSecondary"
-        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textAppearance="@style/TextAppearance.Control.Subtitle"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent" />
+
+    <CheckBox
+        android:id="@+id/favorite"
+        android:visibility="gone"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"/>
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/controls_row.xml b/packages/SystemUI/res/layout/controls_row.xml
index 13a6b36..4cc461a 100644
--- a/packages/SystemUI/res/layout/controls_row.xml
+++ b/packages/SystemUI/res/layout/controls_row.xml
@@ -16,7 +16,7 @@
 -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    orientation="horizontal"
+    android:orientation="horizontal"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_marginBottom="@dimen/control_spacing" />
diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml
index 6a3e95d..40b2476 100644
--- a/packages/SystemUI/res/layout/controls_with_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_with_favorites.xml
@@ -13,7 +13,7 @@
         android:layout_height="wrap_content"
         android:singleLine="true"
         android:gravity="center"
-        android:textSize="25dp"
+        android:textSize="25sp"
         android:textColor="@*android:color/foreground_material_dark"
         android:fontFamily="@*android:string/config_headlineFontFamily"
         app:layout_constraintBottom_toBottomOf="parent"
diff --git a/packages/SystemUI/res/layout/foreground_service_dungeon.xml b/packages/SystemUI/res/layout/foreground_service_dungeon.xml
new file mode 100644
index 0000000..d4e98e2
--- /dev/null
+++ b/packages/SystemUI/res/layout/foreground_service_dungeon.xml
@@ -0,0 +1,61 @@
+<!--
+  ~ 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.
+  -->
+
+<com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/foreground_service_dungeon"
+    android:layout_width="@dimen/qs_panel_width"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal|bottom"
+    android:visibility="visible"
+>
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:orientation="vertical"
+        android:gravity="bottom"
+        android:visibility="visible"
+        android:background="@drawable/notif_dungeon_bg_gradient"
+    >
+
+        <!-- divider view -->
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:background="@color/GM2_grey_200"
+            android:visibility="visible"
+        />
+
+        <TextView
+            android:id="@+id/dungeon_title"
+            android:layout_height="48dp"
+            android:layout_width="match_parent"
+            android:padding="8dp"
+            android:text="Apps active in background"
+            android:textColor="@color/GM2_grey_200"
+        />
+
+        <!--  List containing the actual foreground service notifications  -->
+        <LinearLayout
+            android:id="@+id/entry_list"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="bottom"
+            android:orientation="vertical" >
+        </LinearLayout>
+
+    </LinearLayout>
+</com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView>
diff --git a/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml b/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml
new file mode 100644
index 0000000..a6f1638
--- /dev/null
+++ b/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml
@@ -0,0 +1,43 @@
+<!--
+  ~ 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.
+  -->
+
+<com.android.systemui.statusbar.notification.row.DungeonRow
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/foreground_service_dungeon_row"
+    android:layout_width="match_parent"
+    android:layout_height="48dp"
+    android:padding="8dp"
+    android:clickable="true"
+    android:orientation="horizontal" >
+
+    <com.android.systemui.statusbar.StatusBarIconView
+        android:id="@+id/icon"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:padding="4dp" />
+
+    <TextView
+        android:id="@+id/app_name"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:paddingStart="4dp"
+        android:gravity="center_vertical"
+        android:layout_gravity="center_vertical"
+        android:textColor="@color/GM2_grey_200"
+    />
+
+</com.android.systemui.statusbar.notification.row.DungeonRow>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 823685e..478a93d 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Vergrotingoorleggervenster"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Vergrotingvenster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Vergrotingvensterkontroles"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Vinnige kontroles"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index c324824..08a655a 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"የማጉያ ንብርብር መስኮት"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"የማጉያ መስኮት"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"የማጉያ መስኮት መቆጣጠሪያዎች"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"ፈጣን መቆጣጠሪያዎች"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 113d705..6ceb6cd 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -984,4 +984,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"نافذة تراكب التكبير"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"نافذة التكبير"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"عناصر التحكم في نافذة التكبير"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 975d639..ffefb48 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"বিবৰ্ধন অ’ভাৰলে’ৰ ৱিণ্ড’"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"বিবৰ্ধন ৱিণ্ড’"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"বিবৰ্ধন ৱিণ্ড’ৰ নিয়ন্ত্ৰণসমূহ"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index d98d939..2caffff 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Böyütmə Üst-üstə Düşən Pəncərəsi"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Böyütmə Pəncərəsi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Böyütmə Pəncərəsi Kontrolları"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index ff03bb9..aaa35ff 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -969,4 +969,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Preklopni prozor za uvećanje"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Prozor za uvećanje"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za uvećanje"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Brze kontrole"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 1ebbb5e..619fd1c 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -976,4 +976,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Акно-накладка з павелічэннем"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Акно павелічэння"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Налады акна павелічэння"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 5cfea60..99c1fec 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Прозорец с наслагване за ниво на мащаба"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Прозорец за ниво на мащаба"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Контроли за прозореца за ниво на мащаба"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Бързи контроли"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index f1e49ce..1327cf4 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"ওভারলে উইন্ডো বড় করে দেখা"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"উইন্ডো বড় করে দেখা"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"উইন্ডো কন্ট্রোল বড় করে দেখা"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index cabe87f..7bce70a 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -971,4 +971,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Preklopni prozor za uvećavanje"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Prozor za uvećavanje"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za uvećavanje"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Brze kontrole"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 3e25795..5597f51 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Finestra superposada d\'ampliació"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Finestra d\'ampliació"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Finestra de controls d\'ampliació"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Controls ràpids"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 3c85880..a608c5b 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -974,4 +974,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Překryvné zvětšovací okno"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Zvětšovací okno"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ovládací prvky zvětšovacího okna"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Rychlé ovládací prvky"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index f2ffdaa..9fb7783 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Vindue med overlejret forstørrelse"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Vindue med forstørrelse"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Vindue med forstørrelsesstyring"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Hurtig betjening"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 58caef2..9d83e9c 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -968,4 +968,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Overlay-Vergrößerungsfenster"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Vergrößerungsfenster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Einstellungen für Vergrößerungsfenster"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index d3686c8..b94d10c 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Παράθυρο επικάλυψης μεγέθυνσης"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Παράθυρο μεγέθυνσης"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Στοιχεία ελέγχου παραθύρου μεγέθυνσης"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Στοιχεία γρήγορου ελέγχου"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 4f61daa..df24969 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification overlay window"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Quick controls"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 34a2f19..2d087c9 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification overlay window"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Quick controls"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 4f61daa..df24969 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification overlay window"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Quick controls"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 4f61daa..df24969 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification overlay window"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Quick controls"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 2bb5131..9d4e162 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‏‏‎‎Magnification Overlay Window‎‏‎‎‏‎"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‏‏‎‏‎Magnification Window‎‏‎‎‏‎"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎Magnification Window Controls‎‏‎‎‏‎"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎‎‎‎‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‏‎‏‎Quick Controls‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index e2bf6ef..33689a1 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Ventana superpuesta de ampliación"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Ventana de ampliación"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controles de ampliación de la ventana"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Controles rápidos"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 40d7fe3..458df31 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Ventana de superposición de ampliación"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Ventana de ampliación"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ventana de controles de ampliación"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Controles rápidos"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index d8f546c..4729a0d 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Suurendamisakna ülekate"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Suurendamisaken"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Suurendamisakna juhtelemendid"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 9dceb5f..470a410 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Lupa-leiho gainjarria"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Lupa-leihoa"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Lupa-leihoaren aukerak"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Kontrol bizkorrak"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index d27b420..ae85a26 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"پنجره همپوشانی بزرگ‌نمایی"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"پنجره بزرگ‌نمایی"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"کنترل‌های پنجره بزرگ‌نمایی"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index cf01efd..da22e1c 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Suurennuksen peittoikkuna"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Suurennusikkuna"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Suurennusikkunan ohjaimet"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Pikasäätimet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 5f7a358..d5d3f56 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Fenêtre d\'agrandissement superposée"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Commandes pour la fenêtre d\'agrandissement"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 3c12f66..5c5d353 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Fenêtre de superposition de l\'agrandissement"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Fenêtre des commandes d\'agrandissement"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 0c444e4..2c0eef6 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Ampliación da ventá de superposición"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Ventá de superposición"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controis de ampliación da ventá"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 9dde523..df4254c 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"વિસ્તૃતીકરણ ઓવરલે વિંડો"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"વિસ્તૃતીકરણ વિંડો"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"વિસ્તૃતીકરણ વિંડોના નિયંત્રણો"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 5522605..b62a45e 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -390,8 +390,8 @@
     <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"बैटरी सेवर"</string>
     <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"शाम को चालू होगा"</string>
     <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सुबह तक चालू रहेगी"</string>
-    <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> बजे चालू हाेगी"</string>
-    <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> बजे तक चालू रहेगी"</string>
+    <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> पर चालू हाेगी"</string>
+    <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> तक चालू रहेगी"</string>
     <string name="quick_settings_nfc_label" msgid="1054317416221168085">"एनएफ़सी"</string>
     <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC बंद है"</string>
     <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC चालू है"</string>
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification Overlay Window"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"स्क्रीन को बड़ा करके दिखाने वाली विंडो"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"स्क्रीन को बड़ा करके दिखाने वाली विंडो के नियंत्रण"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 55a0e78..5eefc79 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -969,4 +969,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Prozor preklapanja povećavanja"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Prozor za povećavanje"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za povećavanje"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Brze kontrole"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index a9b2d03..5ce235d 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Nagyítási fedvény ablaka"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Nagyítás ablaka"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Nagyítási vezérlők ablaka"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Gyorsvezérlők"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 78acb2e..4fa7096 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Խոշորացման պատուհանի վրադրում"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Խոշորացման պատուհան"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Խոշորացման պատուհանի կառավարման տարրեր"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Արագ կառավարման տարրեր"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 95c16c8..b3db0f3 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Jendela Overlay Pembesaran"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Jendela Pembesaran"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrol Jendela Pembesaran"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Kontrol Cepat"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 985f62d..56e67ef 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Stækkun yfirglugga"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Stækkunargluggi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Stækkunarstillingar glugga"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Flýtistýringar"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 870e6b7..4230677 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Finestra overlay ingrandimento"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Finestra ingrandimento"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Finestra controlli di ingrandimento"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Controlli rapidi"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 775be02..b20b10f 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -974,4 +974,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"חלון ליצירת שכבת-על להגדלה"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"חלון הגדלה"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"בקרות של חלון ההגדלה"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 7af3f86..b518973 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"拡大オーバーレイ ウィンドウ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"拡大ウィンドウ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"拡大ウィンドウ コントロール"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"クイック コントロール"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 2433f9e..ab0dba8 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"გადიდების გადაფარვის ფანჯარა"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"გადიდების ფანჯარა"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"გადიდების კონტროლის ფანჯარა"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index be93c18..3c44cab 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Ұлғайту терезесін қабаттастыру"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Ұлғайту терезесі"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ұлғайту терезесінің басқару элементтері"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 413125c..480b694 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"វិនដូ​ត្រួតគ្នា​លើ​ការពង្រីក"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"វិនដូ​ការពង្រីក"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"វិនដូគ្រប់គ្រង​​ការពង្រីក"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"ការគ្រប់គ្រង​រហ័ស"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 0b98e06..c65e653 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"ವರ್ಧನೆಯ ಓವರ್‌ಲೇ ವಿಂಡೋ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ವರ್ಧನೆಯ ವಿಂಡೋ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ವರ್ಧನೆಯ ವಿಂಡೋ ನಿಯಂತ್ರಣಗಳು"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 54b055d..fb5818c 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"확대 오버레이 창"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"확대 창"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"확대 창 컨트롤"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index ed6b918..53d780e 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Чоңойтуу терезесин үстүнө коюу"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Чоңойтуу терезеси"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Чоңойтуу терезесин башкаруу каражаттары"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 11c4f7c..48166f3 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"ໜ້າຈໍວາງທັບການຂະຫຍາຍ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ໜ້າຈໍການຂະຫຍາຍ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ການຄວບຄຸມໜ້າຈໍການຂະຫຍາຍ"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"ການຄວບຄຸມດ່ວນ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 7da1174..70bcf37 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -974,4 +974,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Didinimo perdangos langas"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Didinimo langas"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Didinimo lango valdikliai"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Spartieji valdikliai"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index fed86b3..6c28b10 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -969,4 +969,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Palielināšanas pārklājuma logs"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Palielināšanas logs"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Palielināšanas loga vadīklas"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index a02f38e..f81c5ff 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Прозорец за преклопување на зголемувањето"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Прозорец за зголемување"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Контроли на прозорец за зголемување"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Брзи контроли"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 2d3f73f..3ffcbc2 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"മാഗ്നിഫിക്കേഷൻ ഓവർലേ വിൻഡോ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"മാഗ്നിഫിക്കേഷൻ വിൻഡോ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"മാഗ്നിഫിക്കേഷൻ വിൻഡോ നിയന്ത്രണങ്ങൾ"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 85d7ead..87d5fbf 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Томруулалтыг давхарласан цонх"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Томруулалтын цонх"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Томруулалтын цонхны хяналт"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Шуурхай хяналтууд"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 3406edf..d67c0ef 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"मॅग्निफिकेशन ओव्हरले विंडो"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"मॅग्निफिकेशन विंडो"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"मॅग्निफिकेशन विंडो नियंत्रणे"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index eeaacea..e3e9746 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Tetingkap Tindanan Pembesaran"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Tetingkap Pembesaran"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kawalan Tetingkap Pembesaran"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 5ae82ea..9907570 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"ဝင်းဒိုး ထပ်ပိုးလွှာ ချဲ့ခြင်း"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ဝင်းဒိုး ချဲ့ခြင်း"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ဝင်းဒိုး ထိန်းချုပ်မှုများ ချဲ့ခြင်း"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 463b95c..c273cc9 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Overleggsvindu for forstørring"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Forstørringsvindu"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontroller for forstørringsvindu"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Hurtigkontroller"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 21a76a3..dea3311 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"म्याग्निफिकेसन ओभरले विन्डो"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"म्याग्निफिकेसन विन्डो"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"म्याग्निफिकेसन विन्डोका नियन्त्रणहरू"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 3c04015..8b015ab 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Overlay voor vergrotingsvenster"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Vergrotingsvenster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Bediening van vergrotingsvenster"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Snelle bedieningselementen"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 4230275..e274f27 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ଓଭର୍‌ଲେ ୱିଣ୍ଡୋ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ୱିଣ୍ଡୋ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ୱିଣ୍ଡୋ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index f345d6a..28da4ed 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"ਵੱਡਦਰਸ਼ੀਕਰਨ ਓਵਰਲੇ Window"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ਵੱਡਦਰਸ਼ੀਕਰਨ Window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ਵੱਡਦਰਸ਼ੀਕਰਨ Window ਦੇ ਕੰਟਰੋਲ"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 68cb14e..fd48b97 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -909,7 +909,7 @@
     <string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string>
     <string name="notification_channel_screenshot" msgid="7665814998932211997">"Zrzuty ekranu"</string>
     <string name="notification_channel_general" msgid="4384774889645929705">"Wiadomości"</string>
-    <string name="notification_channel_storage" msgid="2720725707628094977">"Pamięć"</string>
+    <string name="notification_channel_storage" msgid="2720725707628094977">"Pamięć wewnętrzna"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"Wskazówki"</string>
     <string name="instant_apps" msgid="8337185853050247304">"Aplikacje błyskawiczne"</string>
     <string name="instant_apps_title" msgid="8942706782103036910">"Aplikacja <xliff:g id="APP">%1$s</xliff:g> działa"</string>
@@ -974,4 +974,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Okno nakładki powiększenia"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Okno powiększenia"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Elementy sterujące okna powiększenia"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Szybkie sterowanie"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index a9101d7..6f61662 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Janela de sobreposição de ampliação"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controles da janela de ampliação"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Controles rápidos"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 1a6e673..be534ae 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Janela de sobreposição da ampliação"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controlos da janela de ampliação"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Controlos rápidos"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index a9101d7..6f61662 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Janela de sobreposição de ampliação"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controles da janela de ampliação"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Controles rápidos"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index e34fa2d..44940bf 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -969,4 +969,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Fereastra de suprapunere pentru mărire"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Fereastra de mărire"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Comenzi pentru fereastra de mărire"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Comenzi rapide"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 0ee159d..6fe2094 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -974,4 +974,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Наложение окна увеличения"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Окно увеличения"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Настройки окна увеличения"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 2c5e1d8..38933ad 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"විශාලන උඩැතිරි කවුළුව"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"විශාලන කවුළුව"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"විශාලනය කිරීමේ කවුළු පාලන"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"ඉක්මන් පාලන"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 93f93a2..5bd709c 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -974,4 +974,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Okno prekrytia priblíženia"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Okno priblíženia"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ovládacie prvky okna priblíženia"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Rýchle ovládacie prvky"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 452827f..7c06fa7 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -974,4 +974,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Prekrivno povečevalno okno"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Povečevalno okno"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrolniki povečevalnega okna"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Hitro upravljanje"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 2dc3cc8..7549611 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Dritarja e mbivendosjes së zmadhimit"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Dritarja e zmadhimit"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrollet e dritares së zmadhimit"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 4127806..6b2d971 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -969,4 +969,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Преклопни прозор за увећање"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Прозор за увећање"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Контроле прозора за увећање"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Брзе контроле"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 3e29fa6..33c2078 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Överlagrat förstoringsfönster"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Förstoringsfönster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Inställningar för förstoringsfönster"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Snabbinställningar"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 9fd4284..bf2640f 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -958,10 +958,11 @@
     <string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Sogeza chini kushoto"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Sogeza chini kulia"</string>
     <string name="bubble_dismiss_text" msgid="7071770411580452911">"Ondoa"</string>
-    <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Umesasisha usogezaji kwenye mfumo. Ili ufanye mabadiliko, nenda kwenye Mipangilio."</string>
+    <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Umesasisha usogezaji kwenye mfumo. Ili ubadilishe, nenda kwenye Mipangilio."</string>
     <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Nenda kwenye mipangilio ili usasishe usogezaji kwenye mfumo"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Hali tuli"</string>
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Dirisha la Kuwekelea Linalokuza"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Dirisha la Ukuzaji"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Vidhibiti vya Dirisha la Ukuzaji"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Vidhibiti vya Haraka"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 55bd472..c5289ec 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification Overlay Window"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"பெரிதாக்கல் சாளரம்"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"பெரிதாக்கல் சாளரக் கட்டுப்பாடுகள்"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 8b9c07c..ed562f3 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -71,7 +71,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"స్క్రీన్‌కు నింపేలా జూమ్ చేయండి"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"స్క్రీన్‌కు నింపేలా విస్తరించండి"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"స్క్రీన్‌షాట్"</string>
-    <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"చిత్రం చొప్పించబడింది"</string>
+    <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"ఇమేజ్ చొప్పించబడింది"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"స్క్రీన్‌షాట్‌ను సేవ్ చేస్తోంది…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"స్క్రీన్‌షాట్‌ను సేవ్ చేస్తోంది…"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"స్క్రీన్‌షాట్ సేవ్ చేయబడింది"</string>
@@ -390,7 +390,7 @@
     <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"బ్యాటరీ సేవర్"</string>
     <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"సూర్యాస్తమయానికి"</string>
     <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"సూర్యోదయం వరకు"</string>
-    <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> వద్ద"</string>
+    <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> కు ఆన్ అవుతుంది"</string>
     <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> వరకు"</string>
     <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC నిలిపివేయబడింది"</string>
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"మాగ్నిఫికేషన్ ఓవర్‌లే విండో"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"మాగ్నిఫికేషన్ విండో"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"మాగ్నిఫికేషన్ నియంత్రణల విండో"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index dd4c321..bb49994 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -20,6 +20,9 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
+    <!-- SystemUIFactory component -->
+    <string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.tv.TvSystemUIFactory</string>
+
     <!-- SystemUI Services: The classes of the stuff to start. -->
     <string-array name="config_systemUIServiceComponents" translatable="false">
         <item>com.android.systemui.util.NotificationChannels</item>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index c5e0123..9e5b2db 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"หน้าต่างการขยายที่วางซ้อน"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"หน้าต่างการขยาย"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"การควบคุมหน้าต่างการขยาย"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"การควบคุมอย่างรวดเร็ว"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 3d343c5..9485a2b 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Window ng Overlay sa Pag-magnify"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Window ng Pag-magnify"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Mga Kontrol sa Pag-magnify ng Window"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Mga Mabilisang Kontrol"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 46e583f..950b66b 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Yer Paylaşımlı Büyütme Penceresi"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Büyütme Penceresi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Büyütme Penceresi Kontrolleri"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Hızlı Kontroller"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 7baad0d..3e1e4a9 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -974,4 +974,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Вікно збільшення з накладанням"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Вікно збільшення"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Елементи керування вікна збільшення"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Елементи швидкого керування"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 97d0a5d..6f3e7ab 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"میگنیفیکیشن اوورلے ونڈو"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"میگنیفکیشن ونڈو"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"میگنیفکیشن ونڈو کنٹرولز"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"فوری کنٹرولز"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index b514f1e..618463e 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -390,7 +390,7 @@
     <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Quvvat tejash"</string>
     <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Kunbotarda yoqish"</string>
     <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Quyosh chiqqunicha"</string>
-    <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> da yoqish"</string>
+    <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> da yoqiladi"</string>
     <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> gacha"</string>
     <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC o‘chiq"</string>
@@ -477,9 +477,9 @@
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Boshqa ko‘rsatilmasin"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Hammasini tozalash"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Boshqarish"</string>
-    <string name="notification_section_header_gentle" msgid="3044910806569985386">"Tovushsiz bildirishnomalar"</string>
+    <string name="notification_section_header_gentle" msgid="3044910806569985386">"Sokin bildirishnomalar"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Suhbatlar"</string>
-    <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Barcha tovushsiz bildirishnomalarni tozalash"</string>
+    <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Barcha sokin bildirishnomalarni tozalash"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bezovta qilinmasin rejimida bildirishnomalar pauza qilingan"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Boshlash"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Bildirishnomalar yo‘q"</string>
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Kattalashtirish oynasining ustidan ochilishi"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Kattalashtirish oynasi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kattalashtirish oynasi sozlamalari"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Tezkor tugmalar"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index c4d7618..6be05aa 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Cửa sổ lớp phủ phóng to"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Cửa sổ phóng to"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Các tùy chọn điều khiển cửa sổ phóng to"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 56edb64..e10abbb 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -964,4 +964,6 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"放大叠加窗口"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"放大窗口"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"放大窗口控件"</string>
+    <!-- no translation found for quick_controls_title (525285759614231333) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 251e23d..a83ce53 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"放大重疊視窗"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"放大視窗"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"放大視窗控制項"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"快速控制介面"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index a7ec598..9032595 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"放大重疊視窗"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"放大視窗"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"放大視窗控制項"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"快速控制項"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 1203b579c..019128b 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -964,4 +964,5 @@
     <string name="magnification_overlay_title" msgid="6584179429612427958">"Iwindi Lembondela Lesikhulisi"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Iwindi Lesikhulisi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Izilawuli Zewindi Lesikhulisi"</string>
+    <string name="quick_controls_title" msgid="525285759614231333">"Izilawuli Ezisheshayo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 33cabc1..9c997e8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1187,7 +1187,27 @@
     <dimen name="control_corner_radius">15dp</dimen>
     <dimen name="control_height">100dp</dimen>
     <dimen name="control_padding">15dp</dimen>
-    <dimen name="control_status_normal">12dp</dimen>
-    <dimen name="control_status_expanded">18dp</dimen>
-    <dimen name="app_icon_size">32dp</dimen>
+    <dimen name="control_status_normal">12sp</dimen>
+    <dimen name="control_status_expanded">18sp</dimen>
+    <dimen name="control_base_item_margin">2dp</dimen>
+    <dimen name="control_status_padding">3dp</dimen>
+
+    <!-- Home Controls management screens -->
+    <dimen name="controls_management_top_padding">48dp</dimen>
+    <dimen name="controls_management_side_padding">8dp</dimen>
+    <dimen name="controls_management_titles_margin">8dp</dimen>
+    <dimen name="controls_management_list_margin">16dp</dimen>
+    <dimen name="controls_title_size">26sp</dimen>
+
+    <dimen name="controls_app_icon_size">32dp</dimen>
+    <dimen name="controls_app_icon_frame_side_padding">8dp</dimen>
+    <dimen name="controls_app_icon_frame_top_padding">4dp</dimen>
+    <dimen name="controls_app_bottom_margin">8dp</dimen>
+    <dimen name="controls_app_text_padding">8dp</dimen>
+    <dimen name="controls_app_divider_height">2dp</dimen>
+    <dimen name="controls_app_divider_side_margin">32dp</dimen>
+
+    <dimen name="controls_card_margin">2dp</dimen>
+
+
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index fb8df5b..82cd5f7 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2547,6 +2547,24 @@
     <!-- Title for Magnification Controls Window [CHAR LIMIT=NONE] -->
     <string name="magnification_controls_title">Magnification Window Controls</string>
 
-    <!-- Quick Controls strings [CHAR LIMIT=30] -->
+    <!-- Quick Controls strings -->
+    <!-- Quick Controls view header [CHAR LIMIT=30] -->
     <string name="quick_controls_title">Quick Controls</string>
+
+    <!-- Controls management providers screen title [CHAR LIMIT=30]-->
+    <string name="controls_providers_title">Add Controls</string>
+    <!-- Controls management providers screen subtitle [CHAR LIMIT=NONE] -->
+    <string name="controls_providers_subtitle">Choose an app from which to add controls</string>
+    <!-- Number of favorites for controls management screen [CHAR LIMIT=NONE]-->
+    <plurals name="controls_number_of_favorites">
+        <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> current favorite.</item>
+        <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> current favorites.</item>
+    </plurals>
+
+    <!-- Controls management controls screen default title [CHAR LIMIT=30] -->
+    <string name="controls_favorite_default_title">Controls</string>
+    <!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] -->
+    <string name="controls_favorite_subtitle">Choose controls for quick access</string>
+
+
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 10f59a4..bcffa8d 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -234,6 +234,37 @@
 
     <style name="TextAppearance.DeviceManagementDialog.Title" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
 
+    <style name="TextAppearance.AuthCredential">
+        <item name="android:gravity">center_horizontal</item>
+        <item name="android:fontFamily">google-sans</item>
+        <item name="android:textAlignment">gravity</item>
+        <item name="android:layout_gravity">top</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.AuthCredential.Title">
+        <item name="android:layout_marginBottom">2dp</item>
+        <item name="android:layout_marginLeft">24dp</item>
+        <item name="android:layout_marginRight">24dp</item>
+        <item name="android:layout_marginTop">16dp</item>
+        <item name="android:textSize">24sp</item>
+    </style>
+
+    <style name="TextAppearance.AuthCredential.Description">
+        <item name="android:layout_marginBottom">12dp</item>
+        <item name="android:layout_marginStart">40dp</item>
+        <item name="android:layout_marginEnd">40dp</item>
+        <item name="android:layout_marginTop">3dp</item>
+        <item name="android:textSize">16sp</item>
+    </style>
+
+    <style name="TextAppearance.AuthCredential.PasswordEntry" parent="@android:style/TextAppearance.DeviceDefault">
+        <item name="android:gravity">center</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:textColor">?android:attr/colorForeground</item>
+        <item name="android:textSize">24sp</item>
+    </style>
+
     <style name="DeviceManagementDialogTitle">
         <item name="android:gravity">center</item>
         <item name="android:textAppearance">@style/TextAppearance.DeviceManagementDialog.Title</item>
@@ -297,6 +328,16 @@
         <item name="android:textColor">?attr/wallpaperTextColor</item>
     </style>
 
+    <style name="LockPatternContainerStyle">
+        <item name="android:maxHeight">400dp</item>
+        <item name="android:maxWidth">420dp</item>
+        <item name="android:minHeight">0dp</item>
+        <item name="android:minWidth">0dp</item>
+        <item name="android:paddingBottom">0dp</item>
+        <item name="android:paddingHorizontal">44dp</item>
+        <item name="android:paddingTop">0dp</item>
+    </style>
+
     <style name="LockPatternStyle">
         <item name="*android:regularColor">?attr/wallpaperTextColor</item>
         <item name="*android:successColor">?attr/wallpaperTextColor</item>
@@ -583,4 +624,28 @@
         <item name="android:background">?android:attr/selectableItemBackground</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
+
+    <!-- Controls styles -->
+    <style name="Theme.ControlsManagement" parent="@android:style/Theme.DeviceDefault.NoActionBar">
+        <item name="android:windowIsTranslucent">false</item>
+    </style>
+
+    <style name="TextAppearance.Control">
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+    </style>
+
+    <style name="TextAppearance.Control.Status">
+        <item name="android:textSize">12sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.Control.Title">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+    <style name="TextAppearance.Control.Subtitle">
+        <item name="android:textSize">12sp</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+    </style>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index eab9706..924d16d 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -89,7 +89,7 @@
             }
 
             @Override
-            public void onEntryRemoved(NotificationEntry entry, int reason, boolean removedByUser) {
+            public void onEntryRemoved(NotificationEntry entry, int reason) {
                 removeNotification(entry.getSbn());
             }
         });
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 044c5a0..7dea7f8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -664,6 +664,7 @@
 
         setTextOrHide(mSubtitleView,
                 mBiometricPromptBundle.getString(BiometricPrompt.KEY_SUBTITLE));
+
         setTextOrHide(mDescriptionView,
                 mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION));
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 8f2cf70..68b05e3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -17,6 +17,7 @@
 package com.android.systemui.biometrics;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.hardware.biometrics.BiometricPrompt;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -28,6 +29,7 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -56,6 +58,7 @@
     private TextView mTitleView;
     private TextView mSubtitleView;
     private TextView mDescriptionView;
+    private ImageView mIconView;
     protected TextView mErrorView;
 
     protected @Utils.CredentialType int mCredentialType;
@@ -176,6 +179,16 @@
         setTextOrHide(mDescriptionView,
                 mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION));
 
+        final boolean isManagedProfile = Utils.isManagedProfile(mContext, mEffectiveUserId);
+        final Drawable image;
+        if (isManagedProfile) {
+            image = getResources().getDrawable(R.drawable.auth_dialog_enterprise,
+                    mContext.getTheme());
+        } else {
+            image = getResources().getDrawable(R.drawable.auth_dialog_lock, mContext.getTheme());
+        }
+        mIconView.setImageDrawable(image);
+
         // Only animate this if we're transitioning from a biometric view.
         if (mShouldAnimateContents) {
             setTranslationY(getResources()
@@ -207,6 +220,7 @@
         mTitleView = findViewById(R.id.title);
         mSubtitleView = findViewById(R.id.subtitle);
         mDescriptionView = findViewById(R.id.description);
+        mIconView = findViewById(R.id.icon);
         mErrorView = findViewById(R.id.error);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index a26cce0..7c07c9d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -429,12 +429,11 @@
                     }
                 });
 
-        mNotificationEntryManager.setNotificationRemoveInterceptor(
+        mNotificationEntryManager.addNotificationRemoveInterceptor(
                 new NotificationRemoveInterceptor() {
                     @Override
-                    public boolean onNotificationRemoveRequested(String key, int reason) {
-                        NotificationEntry entry =
-                                mNotificationEntryManager.getActiveNotificationUnfiltered(key);
+                    public boolean onNotificationRemoveRequested(
+                            String key, NotificationEntry entry, int reason) {
                         return shouldInterceptDismissal(entry, reason);
                     }
                 });
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index b02de45..b3ba2b2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -29,6 +29,7 @@
     fun loadForComponent(componentName: ComponentName, callback: (List<ControlStatus>) -> Unit)
     fun subscribeToFavorites()
     fun changeFavoriteStatus(controlInfo: ControlInfo, state: Boolean)
+    fun countFavoritesForComponent(componentName: ComponentName): Int = 0
     fun unsubscribe()
     fun action(controlInfo: ControlInfo, action: ControlAction)
     fun refreshStatus(componentName: ComponentName, control: Control)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 6ff1cf8..a6f1d84 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -328,6 +328,12 @@
         }
     }
 
+    override fun countFavoritesForComponent(componentName: ComponentName): Int {
+        return synchronized(currentFavorites) {
+            currentFavorites.get(componentName)?.size ?: 0
+        }
+    }
+
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
         pw.println("ControlsController state:")
         pw.println("  Available: $available")
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
index 22c6908..b122439 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.controls.management
 
 import android.content.ComponentName
+import android.content.res.Resources
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
@@ -25,6 +26,7 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
 import androidx.recyclerview.widget.RecyclerView
+import com.android.settingslib.applications.DefaultAppInfo
 import com.android.settingslib.widget.CandidateInfo
 import com.android.systemui.R
 import java.util.concurrent.Executor
@@ -46,7 +48,8 @@
     lifecycle: Lifecycle,
     controlsListingController: ControlsListingController,
     private val layoutInflater: LayoutInflater,
-    private val onAppSelected: (ComponentName?) -> Unit = {}
+    private val onAppSelected: (ComponentName?) -> Unit = {},
+    private val favoritesRenderer: FavoritesRenderer
 ) : RecyclerView.Adapter<AppAdapter.Holder>() {
 
     private var listOfServices = emptyList<CandidateInfo>()
@@ -54,7 +57,9 @@
     private val callback = object : ControlsListingController.ControlsListingCallback {
         override fun onServicesUpdated(list: List<CandidateInfo>) {
             uiExecutor.execute {
-                listOfServices = list
+                listOfServices = list.sortedBy {
+                    it.loadLabel().toString()
+                }
                 notifyDataSetChanged()
             }
         }
@@ -65,7 +70,8 @@
     }
 
     override fun onCreateViewHolder(parent: ViewGroup, i: Int): Holder {
-        return Holder(layoutInflater.inflate(R.layout.app_item, parent, false))
+        return Holder(layoutInflater.inflate(R.layout.controls_app_item, parent, false),
+                favoritesRenderer)
     }
 
     override fun getItemCount() = listOfServices.size
@@ -80,9 +86,10 @@
     /**
      * Holder for binding views in the [RecyclerView]-
      */
-    class Holder(view: View) : RecyclerView.ViewHolder(view) {
+    class Holder(view: View, val favRenderer: FavoritesRenderer) : RecyclerView.ViewHolder(view) {
         private val icon: ImageView = itemView.requireViewById(com.android.internal.R.id.icon)
         private val title: TextView = itemView.requireViewById(com.android.internal.R.id.title)
+        private val favorites: TextView = itemView.requireViewById(R.id.favorites)
 
         /**
          * Bind data to the view
@@ -91,6 +98,19 @@
         fun bindData(data: CandidateInfo) {
             icon.setImageDrawable(data.loadIcon())
             title.text = data.loadLabel()
+            favorites.text = favRenderer.renderFavoritesForComponent(
+                    (data as DefaultAppInfo).componentName)
         }
     }
+}
+
+class FavoritesRenderer(
+    private val resources: Resources,
+    private val favoriteFunction: (ComponentName) -> Int
+) {
+
+    fun renderFavoritesForComponent(component: ComponentName): String {
+        val qty = favoriteFunction(component)
+        return resources.getQuantityString(R.plurals.controls_number_of_favorites, qty, qty)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index e6d3c26..65dcc2b1 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -16,15 +16,20 @@
 
 package com.android.systemui.controls.management
 
+import android.graphics.Rect
+import android.graphics.drawable.Icon
+import android.service.controls.DeviceTypes
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.CheckBox
+import android.widget.ImageView
 import android.widget.TextView
 import androidx.recyclerview.widget.RecyclerView
 import com.android.systemui.R
 import com.android.systemui.controls.ControlStatus
 import com.android.systemui.controls.controller.ControlInfo
+import com.android.systemui.controls.ui.RenderInfo
 
 /**
  * Adapter for binding [Control] information to views.
@@ -43,7 +48,12 @@
     var listOfControls = emptyList<ControlStatus>()
 
     override fun onCreateViewHolder(parent: ViewGroup, i: Int): Holder {
-        return Holder(layoutInflater.inflate(R.layout.control_item, parent, false))
+        return Holder(layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply {
+            layoutParams.apply {
+                width = ViewGroup.LayoutParams.MATCH_PARENT
+            }
+            elevation = 15f
+        })
     }
 
     override fun getItemCount() = listOfControls.size
@@ -56,9 +66,13 @@
      * Holder for binding views in the [RecyclerView]-
      */
     class Holder(view: View) : RecyclerView.ViewHolder(view) {
+        private val icon: ImageView = itemView.requireViewById(R.id.icon)
         private val title: TextView = itemView.requireViewById(R.id.title)
         private val subtitle: TextView = itemView.requireViewById(R.id.subtitle)
-        private val favorite: CheckBox = itemView.requireViewById(R.id.favorite)
+        private val removed: TextView = itemView.requireViewById(R.id.status)
+        private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply {
+            visibility = View.VISIBLE
+        }
 
         /**
          * Bind data to the view
@@ -68,9 +82,11 @@
          *                 pre-populated with the [Control] information and the new favorite status.
          */
         fun bindData(data: ControlStatus, callback: (ControlInfo.Builder, Boolean) -> Unit) {
+            val renderInfo = getRenderInfo(data.control.deviceType, data.favorite)
             title.text = data.control.title
             subtitle.text = data.control.subtitle
             favorite.isChecked = data.favorite
+            removed.text = if (data.removed) "Removed" else ""
             favorite.setOnClickListener {
                 val infoBuilder = ControlInfo.Builder().apply {
                     controlId = data.control.controlId
@@ -79,6 +95,25 @@
                 }
                 callback(infoBuilder, favorite.isChecked)
             }
+            itemView.setOnClickListener {
+                favorite.performClick()
+            }
+            applyRenderInfo(renderInfo)
+        }
+
+        private fun getRenderInfo(
+            @DeviceTypes.DeviceType deviceType: Int,
+            favorite: Boolean
+        ): RenderInfo {
+            return RenderInfo.lookup(deviceType, favorite)
+        }
+
+        private fun applyRenderInfo(ri: RenderInfo) {
+            val context = itemView.context
+            val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
+
+            icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
+            icon.setImageTintList(fg)
         }
     }
 
@@ -86,4 +121,23 @@
         listOfControls = list
         notifyDataSetChanged()
     }
+}
+
+class MarginItemDecorator(
+    private val topMargin: Int,
+    private val sideMargins: Int
+) : RecyclerView.ItemDecoration() {
+
+    override fun getItemOffsets(
+        outRect: Rect,
+        view: View,
+        parent: RecyclerView,
+        state: RecyclerView.State
+    ) {
+        outRect.apply {
+            top = topMargin
+            left = sideMargins
+            right = sideMargins
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 7ee4fd5..be52583 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -20,8 +20,10 @@
 import android.content.ComponentName
 import android.os.Bundle
 import android.view.LayoutInflater
-import androidx.recyclerview.widget.LinearLayoutManager
+import android.widget.TextView
+import androidx.recyclerview.widget.GridLayoutManager
 import androidx.recyclerview.widget.RecyclerView
+import com.android.systemui.R
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.controls.controller.ControlInfo
 import com.android.systemui.controls.controller.ControlsControllerImpl
@@ -44,6 +46,7 @@
 
     private lateinit var recyclerView: RecyclerView
     private lateinit var adapter: ControlAdapter
+    private var component: ComponentName? = null
 
     private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
         private val startingUser = controller.currentUserId
@@ -56,10 +59,9 @@
         }
     }
 
-    private var component: ComponentName? = null
-
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+        setContentView(R.layout.controls_management)
 
         val app = intent.getCharSequenceExtra(EXTRA_APP)
         component = intent.getParcelableExtra<ComponentName>(EXTRA_COMPONENT)
@@ -72,17 +74,17 @@
             }
         } ?: { _, _ -> Unit }
 
-        recyclerView = RecyclerView(applicationContext)
+        recyclerView = requireViewById(R.id.list)
         adapter = ControlAdapter(LayoutInflater.from(applicationContext), callback)
         recyclerView.adapter = adapter
-        recyclerView.layoutManager = LinearLayoutManager(applicationContext)
+        recyclerView.layoutManager = GridLayoutManager(applicationContext, 2)
+        val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin)
+        recyclerView.addItemDecoration(MarginItemDecorator(margin, margin))
 
-        if (app != null) {
-            setTitle("Controls for $app")
-        } else {
-            setTitle("Controls")
-        }
-        setContentView(recyclerView)
+        requireViewById<TextView>(R.id.title).text = app?.let { it }
+                ?: resources.getText(R.string.controls_favorite_default_title)
+        requireViewById<TextView>(R.id.subtitle).text =
+                resources.getText(R.string.controls_favorite_subtitle)
 
         currentUserTracker.startTracking()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index 5ff949c..645e929 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -20,9 +20,12 @@
 import android.content.Intent
 import android.os.Bundle
 import android.view.LayoutInflater
+import android.widget.TextView
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
+import com.android.systemui.R
 import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.settings.CurrentUserTracker
@@ -37,6 +40,7 @@
     @Main private val executor: Executor,
     @Background private val backExecutor: Executor,
     private val listingController: ControlsListingController,
+    private val controlsController: ControlsController,
     broadcastDispatcher: BroadcastDispatcher
 ) : LifecycleActivity() {
 
@@ -58,13 +62,19 @@
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+        setContentView(R.layout.controls_management)
 
-        recyclerView = RecyclerView(applicationContext)
+        recyclerView = requireViewById(R.id.list)
         recyclerView.adapter = AppAdapter(executor, lifecycle, listingController,
-                LayoutInflater.from(this), ::launchFavoritingActivity)
+                LayoutInflater.from(this), ::launchFavoritingActivity,
+                FavoritesRenderer(resources, controlsController::countFavoritesForComponent))
         recyclerView.layoutManager = LinearLayoutManager(applicationContext)
 
-        setContentView(recyclerView)
+        requireViewById<TextView>(R.id.title).text =
+                resources.getText(R.string.controls_providers_title)
+        requireViewById<TextView>(R.id.subtitle).text =
+                resources.getText(R.string.controls_providers_subtitle)
+
         currentUserTracker.startTracking()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 5519e32..fad2d94 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -36,8 +36,8 @@
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.R
 
-public const val MIN_LEVEL = 0
-public const val MAX_LEVEL = 10000
+const val MIN_LEVEL = 0
+const val MAX_LEVEL = 10000
 
 class ControlViewHolder(
     val layout: ViewGroup,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
index 24c8020..093c99f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -50,7 +50,7 @@
 private const val BUCKET_SIZE = 1000
 private const val THERMOSTAT_RANGE = DeviceTypes.TYPE_THERMOSTAT * BUCKET_SIZE
 
-public val deviceColorMap = mapOf<Int, Pair<Int, Int>>(
+private val deviceColorMap = mapOf<Int, Pair<Int, Int>>(
     (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to
         Pair(R.color.thermo_heat_foreground, R.color.thermo_heat_background),
     (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to
@@ -60,7 +60,7 @@
         Pair(R.color.control_foreground, R.color.control_background)
 }
 
-public val deviceIconMap = mapOf<Int, IconState>(
+private val deviceIconMap = mapOf<Int, IconState>(
     THERMOSTAT_RANGE to IconState(
         R.drawable.ic_device_thermostat_gm2_24px,
         R.drawable.ic_device_thermostat_gm2_24px
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java
index 18fe3ec..5025246 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java
@@ -25,7 +25,6 @@
  */
 @Module(includes = {DefaultActivityBinder.class,
                     DefaultBroadcastReceiverBinder.class,
-                    DefaultServiceBinder.class,
-                    SystemUIBinder.class})
+                    DefaultServiceBinder.class})
 public abstract class DefaultComponentBinder {
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 5c171e4..a57ec5b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -56,7 +56,7 @@
  * overridden by the System UI implementation.
  */
 @Module(includes = {DividerModule.class})
-abstract class SystemUIDefaultModule {
+public abstract class SystemUIDefaultModule {
 
     @Singleton
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index e8509b3..3bf5ad7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -45,6 +45,7 @@
         DependencyBinder.class,
         SystemServicesModule.class,
         SystemUIFactory.ContextHolder.class,
+        SystemUIBinder.class,
         SystemUIModule.class,
         SystemUIDefaultModule.class})
 public interface SystemUIRootComponent {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSColorController.kt b/packages/SystemUI/src/com/android/systemui/qs/QSColorController.kt
deleted file mode 100644
index 3f0c5bb..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSColorController.kt
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs
-
-import android.content.ContentResolver
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
-import android.os.Looper
-import android.provider.Settings
-import android.util.Log
-import com.android.systemui.plugins.qs.QSTile
-
-private const val TAG = "QSColorController"
-private const val QS_COLOR_ICON = "qs_color_icon"
-private const val QS_COLOR_ENABLED = "qs_color_enabled"
-private const val QS_COLOR_OVERRIDDEN_TILES = "qs_color_overridden_tiles"
-private val qsColorIconUri = Settings.System.getUriFor(QS_COLOR_ICON)
-private val qsColorEnabledUri = Settings.System.getUriFor(QS_COLOR_ENABLED)
-class QSColorController private constructor() {
-
-    private var overrideColor = false
-    private var colorIcon = false
-    private lateinit var colorCache: SettingBackedMap
-
-    companion object {
-        val instance = QSColorController()
-        internal fun overrideColor() = instance.overrideColor
-        internal fun colorIcon() = instance.colorIcon
-
-        @QSTile.ColorTile
-        private fun getColorFromSetting(setting: String): Int {
-            return when (setting.toLowerCase()) {
-                "red" -> QSTile.COLOR_TILE_RED
-                "blue" -> QSTile.COLOR_TILE_BLUE
-                "green" -> QSTile.COLOR_TILE_GREEN
-                "yellow" -> QSTile.COLOR_TILE_YELLOW
-                else -> QSTile.COLOR_TILE_ACCENT
-            }
-        }
-    }
-
-    private fun getBooleanSetting(key: String, default: Boolean = false): Boolean =
-            try {
-                Settings.System.getInt(contentResolver, key) != 0
-            } catch (_: Settings.SettingNotFoundException) {
-                default
-            }
-
-    private lateinit var tileHost: QSHost
-    private lateinit var contentResolver: ContentResolver
-
-    fun initQSTileHost(host: QSHost) {
-        tileHost = host
-        contentResolver = tileHost.context.contentResolver
-        colorCache = SettingBackedMap(contentResolver, mutableMapOf())
-        colorIcon = getBooleanSetting(QS_COLOR_ICON)
-        overrideColor = getBooleanSetting(QS_COLOR_ENABLED)
-        readExistingSettings()
-        contentResolver.registerContentObserver(qsColorEnabledUri, true, settingsListener)
-        contentResolver.registerContentObserver(qsColorIconUri, false, settingsListener)
-    }
-
-    private fun readExistingSettings() {
-        Settings.System.getString(contentResolver, QS_COLOR_OVERRIDDEN_TILES)?.split(",")
-                ?.mapNotNull { spec ->
-            Settings.System.getString(contentResolver, "$QS_COLOR_ENABLED/$spec")?.let {
-                spec to it
-            }
-        }?.forEach {
-            modifyTileColor(it.first, getColorFromSetting(it.second))
-        }
-    }
-
-    private val settingsListener = object : ContentObserver(Handler(Looper.getMainLooper())) {
-        override fun onChange(selfChange: Boolean, uri: Uri) {
-            super.onChange(selfChange, uri)
-            when (uri) {
-                qsColorIconUri -> colorIcon = getBooleanSetting(QS_COLOR_ICON)
-                qsColorEnabledUri -> overrideColor = getBooleanSetting(QS_COLOR_ENABLED)
-                else -> {
-                    uri.path?.drop("/system/".length)?.let {
-                        val color = getColorFromSetting(
-                                Settings.System.getString(contentResolver, it) ?: "accent")
-                        val tileSpec = uri.lastPathSegment ?: ""
-                        modifyTileColor(tileSpec, color)
-                    }
-                }
-            }
-        }
-    }
-
-    private fun modifyTileColor(spec: String, @QSTile.ColorTile color: Int) {
-        Log.w(TAG, "Setting color of tile $spec to $color")
-        colorCache.put(spec, color)
-        tileHost.tiles.firstOrNull { it.tileSpec == spec }?.setColor(color)
-    }
-
-    fun applyColorToTile(tile: QSTile) {
-        colorCache.get(tile.tileSpec)?.let {
-            modifyTileColor(tile.tileSpec, it)
-        }
-    }
-
-    fun applyColorToAllTiles() = tileHost.tiles.forEach(::applyColorToTile)
-
-    fun destroy() {
-        contentResolver.unregisterContentObserver(settingsListener)
-    }
-
-    class SettingBackedMap(
-        private val contentResolver: ContentResolver,
-        private val map: MutableMap<String, Int>
-    ) : MutableMap<String, @QSTile.ColorTile Int> by map {
-        override fun put(key: String, @QSTile.ColorTile value: Int): Int? {
-            return map.put(key, value).also {
-                Settings.System.putString(contentResolver, QS_COLOR_OVERRIDDEN_TILES,
-                        map.filterValues { it != QSTile.COLOR_TILE_ACCENT }
-                                .keys
-                                .joinToString(","))
-            }
-        }
-    }
-}
-fun overrideColor() = QSColorController.overrideColor()
-fun colorIcon() = QSColorController.colorIcon()
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index f71150f..00e09f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -90,8 +90,6 @@
     private int mCurrentUser;
     private final Optional<StatusBar> mStatusBarOptional;
 
-    private QSColorController mQSColorController = QSColorController.Companion.getInstance();
-
     @Inject
     public QSTileHost(Context context,
             StatusBarIconController iconController,
@@ -127,8 +125,6 @@
             // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
             mAutoTiles = autoTiles.get();
         });
-
-        mQSColorController.initQSTileHost(this);
     }
 
     public StatusBarIconController getIconController() {
@@ -142,8 +138,6 @@
         mServices.destroy();
         mPluginManager.removePluginListener(this);
         mDumpController.unregisterDumpable(this);
-
-        mQSColorController.destroy();
     }
 
     @Override
@@ -280,8 +274,6 @@
                 mCallbacks.get(i).onTilesChanged();
             }
         }
-
-        mQSColorController.applyColorToAllTiles();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 88c7964..31526bf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -14,12 +14,10 @@
 
 package com.android.systemui.qs.tileimpl;
 
-import static com.android.systemui.qs.QSColorControllerKt.colorIcon;
 import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ArgbEvaluator;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -150,15 +148,10 @@
             iv.clearColorFilter();
         }
         if (state.state != mState) {
-            int color = getColor(state.state, state.colorActive);
+            int color = getColor(state.state);
             mState = state.state;
             if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
-                if (colorIcon()) {
-                    animateColor(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
-                } else {
-                    animateGrayScale(mTint, color, iv,
-                            () -> updateIcon(iv, state, allowAnimations));
-                }
+                animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
                 mTint = color;
             } else {
                 if (iv instanceof AlphaControlledSlashImageView) {
@@ -175,12 +168,8 @@
         }
     }
 
-    protected int getColor(int state, int colorActive) {
-        return getColorForState(getContext(), state, colorActive);
-    }
-
     protected int getColor(int state) {
-        return getColor(state, -1);
+        return getColorForState(getContext(), state);
     }
 
     private void animateGrayScale(int fromColor, int toColor, ImageView iv,
@@ -217,37 +206,6 @@
         }
     }
 
-    private void animateColor(int fromColor, int toColor, ImageView iv,
-            final Runnable endRunnable) {
-        if (iv instanceof AlphaControlledSlashImageView) {
-            ((AlphaControlledSlashImageView) iv)
-                    .setFinalImageTintList(ColorStateList.valueOf(toColor));
-        }
-        if (mAnimationEnabled && ValueAnimator.areAnimatorsEnabled()) {
-
-
-            ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
-            anim.setDuration(QS_ANIM_LENGTH);
-            anim.addUpdateListener(animation -> {
-                float fraction = animation.getAnimatedFraction();
-                int color = (int) ArgbEvaluator.getInstance().evaluate(fraction, fromColor,
-                        toColor);
-
-                setTint(iv, color);
-            });
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    endRunnable.run();
-                }
-            });
-            anim.start();
-        } else {
-            setTint(iv, toColor);
-            endRunnable.run();
-        }
-    }
-
     public static void setTint(ImageView iv, int color) {
         iv.setImageTintList(ColorStateList.valueOf(color));
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 8b7f280..2fe64d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -13,8 +13,6 @@
  */
 package com.android.systemui.qs.tileimpl;
 
-import static com.android.systemui.qs.QSColorControllerKt.colorIcon;
-import static com.android.systemui.qs.QSColorControllerKt.overrideColor;
 import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
 
 import android.animation.ValueAnimator;
@@ -76,8 +74,6 @@
     private int mCircleColor;
     private int mBgSize;
 
-    private final boolean mQsColors = overrideColor();
-    private final boolean mQSIcons = colorIcon();
 
     public QSTileBaseView(Context context, QSIconView icon) {
         this(context, icon, false);
@@ -212,7 +208,7 @@
     }
 
     protected void handleStateChanged(QSTile.State state) {
-        int circleColor = getCircleColor(state.state, mQsColors ? state.colorActive : -1);
+        int circleColor = getCircleColor(state.state);
         boolean allowAnimations = animationsEnabled();
         if (circleColor != mCircleColor) {
             if (allowAnimations) {
@@ -259,11 +255,10 @@
         return mLocInScreen[1] >= -getHeight();
     }
 
-    private int getCircleColor(int state, int colorActive) {
+    private int getCircleColor(int state) {
         switch (state) {
             case Tile.STATE_ACTIVE:
-                int color = (colorActive == -1) ? mColorActive : colorActive;
-                return mQsColors && mQSIcons ? Utils.applyAlpha(0.5f, color) : color;
+                return mColorActive;
             case Tile.STATE_INACTIVE:
             case Tile.STATE_UNAVAILABLE:
                 return mColorDisabled;
@@ -273,10 +268,6 @@
         }
     }
 
-    private int getCircleColor(int state) {
-        return getCircleColor(state, -1);
-    }
-
     @Override
     public void setClickable(boolean clickable) {
         super.setClickable(clickable);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 1d37911..e1b61c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -26,7 +26,6 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_BAR_STATE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-import static com.android.systemui.qs.QSColorControllerKt.colorIcon;
 
 import android.app.ActivityManager;
 import android.content.Context;
@@ -55,7 +54,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.Prefs;
-import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
@@ -432,10 +430,6 @@
     public abstract CharSequence getTileLabel();
 
     public static int getColorForState(Context context, int state) {
-        return getColorForState(context, state, -1);
-    }
-
-    public static int getColorForState(Context context, int state, int colorActive) {
         switch (state) {
             case Tile.STATE_UNAVAILABLE:
                 return Utils.getDisabled(context,
@@ -443,25 +437,13 @@
             case Tile.STATE_INACTIVE:
                 return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondary);
             case Tile.STATE_ACTIVE:
-                return getActiveColor(context, colorActive);
+                return Utils.getColorAttrDefaultColor(context, android.R.attr.colorPrimary);
             default:
                 Log.e("QSTile", "Invalid state " + state);
                 return 0;
         }
     }
 
-    private static int getActiveColor(Context context, int colorActive) {
-        if (colorIcon()) {
-            if (colorActive == -1) {
-                return Utils.getColorAccentDefaultColor(context);
-            } else {
-                return colorActive;
-            }
-        } else {
-            return Utils.getColorAttrDefaultColor(context, android.R.attr.colorPrimary);
-        }
-    }
-
     protected final class H extends Handler {
         private static final int ADD_CALLBACK = 1;
         private static final int CLICK = 2;
@@ -645,27 +627,4 @@
         pw.println(this.getClass().getSimpleName() + ":");
         pw.print("    "); pw.println(getState().toString());
     }
-
-    @Override
-    public void setColor(@ColorTile int color) {
-        int resId;
-        switch(color) {
-            case COLOR_TILE_RED:
-                resId = R.color.GM2_red_500;
-                break;
-            case COLOR_TILE_BLUE:
-                resId = R.color.GM2_blue_500;
-                break;
-            case COLOR_TILE_GREEN:
-                resId = R.color.GM2_green_500;
-                break;
-            case COLOR_TILE_YELLOW:
-                resId = R.color.GM2_yellow_500;
-                break;
-            default:
-                resId = -1;
-        }
-        mTmpState.colorActive = resId == -1 ? -1 : mContext.getColor(resId);
-        refreshState();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java
index 930116e..caa1e2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java
@@ -16,8 +16,12 @@
 
 package com.android.systemui.statusbar;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.service.notification.NotificationListenerService;
 
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
 /**
  * Interface for anything that may need to prevent notifications from being removed. This is
  * similar to a {@link NotificationLifetimeExtender} in the sense that it extends the life of
@@ -30,11 +34,15 @@
     /**
      * Called when a notification has been removed.
      *
-     * @param key the entry key of the notification being removed.
+     * @param key the key of the notification being removed. Never null
+     * @param entry the entry of the notification being removed.
      * @param removeReason why the notification is being removed, e.g.
      * {@link NotificationListenerService#REASON_CANCEL} or 0 if unknown.
      *
      * @return true if the removal should be ignored, false otherwise.
      */
-    boolean onNotificationRemoveRequested(String key, int removeReason);
+    boolean onNotificationRemoveRequested(
+            @NonNull String key,
+            @Nullable NotificationEntry entry,
+            int removeReason);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 8d4a9ef..37f9f88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -34,6 +34,7 @@
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -81,6 +82,7 @@
     private final BubbleController mBubbleController;
     private final DynamicPrivacyController mDynamicPrivacyController;
     private final KeyguardBypassController mBypassController;
+    private final ForegroundServiceSectionController mFgsSectionController;
     private final Context mContext;
 
     private NotificationPresenter mPresenter;
@@ -101,7 +103,9 @@
             NotificationEntryManager notificationEntryManager,
             KeyguardBypassController bypassController,
             BubbleController bubbleController,
-            DynamicPrivacyController privacyController) {
+            DynamicPrivacyController privacyController,
+            ForegroundServiceSectionController fgsSectionController
+    ) {
         mContext = context;
         mHandler = mainHandler;
         mLockscreenUserManager = notificationLockscreenUserManager;
@@ -110,6 +114,7 @@
         mVisualStabilityManager = visualStabilityManager;
         mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
         mEntryManager = notificationEntryManager;
+        mFgsSectionController = fgsSectionController;
         Resources res = context.getResources();
         mAlwaysExpandNonGroupedNotification =
                 res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
@@ -140,7 +145,8 @@
             boolean hideMedia = Utils.useQsMediaPlayer(mContext);
             if (ent.isRowDismissed() || ent.isRowRemoved()
                     || (ent.isMediaNotification() && hideMedia)
-                    || mBubbleController.isBubbleNotificationSuppressedFromShade(ent)) {
+                    || mBubbleController.isBubbleNotificationSuppressedFromShade(ent)
+                    || mFgsSectionController.hasEntry(ent)) {
                 // we don't want to update removed notifications because they could
                 // temporarily become children if they were isolated before.
                 continue;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt
new file mode 100644
index 0000000..b1d6b40
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.systemui.statusbar.notification
+
+import android.content.Context
+import android.provider.DeviceConfig
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_ALLOW_FGS_DISMISSAL
+import com.android.systemui.util.DeviceConfigProxy
+import javax.inject.Inject
+import javax.inject.Singleton
+
+private var sIsEnabled: Boolean? = null
+
+/**
+ * Feature controller for NOTIFICATIONS_ALLOW_FGS_DISMISSAL config.
+ */
+// TODO: this is really boilerplatey, make a base class that just wraps the device config
+@Singleton
+class ForegroundServiceDismissalFeatureController @Inject constructor(
+    val proxy: DeviceConfigProxy,
+    val context: Context
+) {
+    fun isForegroundServiceDismissalEnabled(): Boolean {
+        return isEnabled(proxy)
+    }
+}
+
+private fun isEnabled(proxy: DeviceConfigProxy): Boolean {
+    if (sIsEnabled == null) {
+        sIsEnabled = proxy.getBoolean(
+                DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_ALLOW_FGS_DISMISSAL, false)
+    }
+
+    return sIsEnabled!!
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 4a22831..6bb377e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -135,6 +135,7 @@
     private final NotificationGroupManager mGroupManager;
     private final NotificationRankingManager mRankingManager;
     private final FeatureFlags mFeatureFlags;
+    private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
 
     private NotificationPresenter mPresenter;
     private RankingMap mLatestRankingMap;
@@ -144,7 +145,7 @@
     final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
             = new ArrayList<>();
     private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
-    private NotificationRemoveInterceptor mRemoveInterceptor;
+    private final List<NotificationRemoveInterceptor> mRemoveInterceptors = new ArrayList<>();
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -157,6 +158,14 @@
                 pw.println(entry.getSbn());
             }
         }
+        pw.println("  Remove interceptors registered:");
+        for (NotificationRemoveInterceptor interceptor : mRemoveInterceptors) {
+            pw.println("    " + interceptor.getClass().getSimpleName());
+        }
+        pw.println("  Lifetime extenders registered:");
+        for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
+            pw.println("    " + extender.getClass().getSimpleName());
+        }
         pw.println("  Lifetime-extended notifications:");
         if (mRetainedNotifications.isEmpty()) {
             pw.println("    None");
@@ -178,7 +187,8 @@
             FeatureFlags featureFlags,
             Lazy<NotificationRowBinder> notificationRowBinderLazy,
             Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
-            LeakDetector leakDetector) {
+            LeakDetector leakDetector,
+            ForegroundServiceDismissalFeatureController fgsFeatureController) {
         mNotifLog = notifLog;
         mGroupManager = groupManager;
         mRankingManager = rankingManager;
@@ -187,6 +197,7 @@
         mNotificationRowBinderLazy = notificationRowBinderLazy;
         mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
         mLeakDetector = leakDetector;
+        mFgsFeatureController = fgsFeatureController;
     }
 
     /** Once called, the NEM will start processing notification events from system server. */
@@ -207,9 +218,14 @@
         mNotificationEntryListeners.remove(listener);
     }
 
-    /** Sets the {@link NotificationRemoveInterceptor}. */
-    public void setNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
-        mRemoveInterceptor = interceptor;
+    /** Add a {@link NotificationRemoveInterceptor}. */
+    public void addNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
+        mRemoveInterceptors.add(interceptor);
+    }
+
+    /** Remove a {@link NotificationRemoveInterceptor} */
+    public void removeNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
+        mRemoveInterceptors.remove(interceptor);
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
@@ -398,14 +414,16 @@
             boolean removedByUser,
             int reason) {
 
-        if (mRemoveInterceptor != null
-                && mRemoveInterceptor.onNotificationRemoveRequested(key, reason)) {
-            // Remove intercepted; log and skip
-            mNotifLog.log(NotifEvent.REMOVE_INTERCEPTED);
-            return;
+        final NotificationEntry entry = getActiveNotificationUnfiltered(key);
+
+        for (NotificationRemoveInterceptor interceptor : mRemoveInterceptors) {
+            if (interceptor.onNotificationRemoveRequested(key, entry, reason)) {
+                // Remove intercepted; log and skip
+                mNotifLog.log(NotifEvent.REMOVE_INTERCEPTED);
+                return;
+            }
         }
 
-        final NotificationEntry entry = getActiveNotificationUnfiltered(key);
         boolean lifetimeExtended = false;
 
         // Notification was canceled before it got inflated
@@ -528,7 +546,10 @@
         Ranking ranking = new Ranking();
         rankingMap.getRanking(key, ranking);
 
-        NotificationEntry entry = new NotificationEntry(notification, ranking);
+        NotificationEntry entry = new NotificationEntry(
+                notification,
+                ranking,
+                mFgsFeatureController.isForegroundServiceDismissalEnabled());
 
         mLeakDetector.trackInstance(entry);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
index eaa9d78..7fe229c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.statusbar.notification.collection;
 
+import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED;
+
 import java.util.Arrays;
 import java.util.List;
 
@@ -132,8 +135,20 @@
                         .append(" ");
             }
 
+            if (notifEntry.mCancellationReason != REASON_NOT_CANCELED) {
+                rksb.append("cancellationReason=")
+                        .append(notifEntry.mCancellationReason)
+                        .append(" ");
+            }
+
             if (notifEntry.hasInflationError()) {
-                rksb.append("hasInflationError ");
+                rksb.append("(!)hasInflationError ");
+            }
+
+            if (notifEntry.getDismissState() != NOT_DISMISSED) {
+                rksb.append("dismissState=")
+                        .append(notifEntry.getDismissState())
+                        .append(" ");
             }
 
             String rkString = rksb.toString();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 1b61703..3b2fe94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -35,9 +35,14 @@
 import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
 import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
 
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.DISMISSED;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
+
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.IntDef;
 import android.annotation.MainThread;
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.RemoteException;
 import android.service.notification.NotificationListenerService.Ranking;
@@ -45,6 +50,8 @@
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.DumpController;
 import com.android.systemui.Dumpable;
@@ -172,15 +179,65 @@
     /**
      * Dismiss a notification on behalf of the user.
      */
-    void dismissNotification(
-            NotificationEntry entry,
-            @CancellationReason int reason,
-            @NonNull DismissedByUserStats stats) {
+    void dismissNotification(NotificationEntry entry, @NonNull DismissedByUserStats stats) {
         Assert.isMainThread();
-        Objects.requireNonNull(stats);
+        requireNonNull(stats);
         checkForReentrantCall();
 
-        removeNotification(entry.getKey(), null, reason, stats);
+        if (entry != mNotificationSet.get(entry.getKey())) {
+            throw new IllegalStateException("Invalid entry: " + entry.getKey());
+        }
+
+        if (entry.getDismissState() == DISMISSED) {
+            return;
+        }
+
+        // Optimistically mark the notification as dismissed -- we'll wait for the signal from
+        // system server before removing it from our notification set.
+        entry.setDismissState(DISMISSED);
+        mLogger.logNotifDismissed(entry.getKey());
+
+        List<NotificationEntry> canceledEntries = new ArrayList<>();
+
+        if (isCanceled(entry)) {
+            canceledEntries.add(entry);
+        } else {
+            // Ask system server to remove it for us
+            try {
+                mStatusBarService.onNotificationClear(
+                        entry.getSbn().getPackageName(),
+                        entry.getSbn().getTag(),
+                        entry.getSbn().getId(),
+                        entry.getSbn().getUser().getIdentifier(),
+                        entry.getSbn().getKey(),
+                        stats.dismissalSurface,
+                        stats.dismissalSentiment,
+                        stats.notificationVisibility);
+            } catch (RemoteException e) {
+                // system process is dead if we're here.
+            }
+
+            // Also mark any children as dismissed as system server will auto-dismiss them as well
+            if (entry.getSbn().getNotification().isGroupSummary()) {
+                for (NotificationEntry otherEntry : mNotificationSet.values()) {
+                    if (otherEntry.getSbn().getGroupKey().equals(entry.getSbn().getGroupKey())
+                            && otherEntry.getDismissState() != DISMISSED) {
+                        otherEntry.setDismissState(PARENT_DISMISSED);
+                        if (isCanceled(otherEntry)) {
+                            canceledEntries.add(otherEntry);
+                        }
+                    }
+                }
+            }
+        }
+
+        // Immediately remove any dismissed notifs that have already been canceled by system server
+        // (probably due to being lifetime-extended up until this point).
+        for (NotificationEntry canceledEntry : canceledEntries) {
+            tryRemoveNotification(canceledEntry);
+        }
+
+        rebuildList();
     }
 
     private void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
@@ -208,7 +265,15 @@
         Assert.isMainThread();
 
         mLogger.logNotifRemoved(sbn.getKey(), reason);
-        removeNotification(sbn.getKey(), rankingMap, reason, null);
+
+        final NotificationEntry entry = mNotificationSet.get(sbn.getKey());
+        if (entry == null) {
+            throw new IllegalStateException("No notification to remove with key " + sbn.getKey());
+        }
+        entry.mCancellationReason = reason;
+        applyRanking(rankingMap);
+        tryRemoveNotification(entry);
+        rebuildList();
     }
 
     private void onNotificationRankingUpdate(RankingMap rankingMap) {
@@ -242,9 +307,12 @@
             // Update to an existing entry
             mLogger.logNotifUpdated(sbn.getKey());
 
-            // Notification is updated so it is essentially re-added and thus alive again.  Don't
+            cancelLocalDismissal(entry);
+
+            // Notification is updated so it is essentially re-added and thus alive again. Don't
             // need to keep its lifetime extended.
             cancelLifetimeExtension(entry);
+            entry.mCancellationReason = REASON_NOT_CANCELED;
 
             entry.setSbn(sbn);
             if (rankingMap != null) {
@@ -255,59 +323,42 @@
         }
     }
 
-    private void removeNotification(
-            String key,
-            @Nullable RankingMap rankingMap,
-            @CancellationReason int reason,
-            @Nullable DismissedByUserStats dismissedByUserStats) {
-
-        NotificationEntry entry = mNotificationSet.get(key);
-        if (entry == null) {
-            throw new IllegalStateException("No notification to remove with key " + key);
+    /**
+     * Tries to remove a notification from the notification set. This removal may be blocked by
+     * lifetime extenders. Does not trigger a rebuild of the list; caller must do that manually.
+     *
+     * @return True if the notification was removed, false otherwise.
+     */
+    private boolean tryRemoveNotification(NotificationEntry entry) {
+        if (mNotificationSet.get(entry.getKey()) != entry) {
+            throw new IllegalStateException("No notification to remove with key " + entry.getKey());
         }
 
-        entry.mLifetimeExtenders.clear();
-        mAmDispatchingToOtherCode = true;
-        for (NotifLifetimeExtender extender : mLifetimeExtenders) {
-            if (extender.shouldExtendLifetime(entry, reason)) {
-                entry.mLifetimeExtenders.add(extender);
-            }
+        if (!isCanceled(entry)) {
+            throw new IllegalStateException("Cannot remove notification " + entry.getKey()
+                        + ": has not been marked for removal");
         }
-        mAmDispatchingToOtherCode = false;
+
+        if (isDismissedByUser(entry)) {
+            // User-dismissed notifications cannot be lifetime-extended
+            cancelLifetimeExtension(entry);
+        } else {
+            updateLifetimeExtension(entry);
+        }
 
         if (!isLifetimeExtended(entry)) {
             mNotificationSet.remove(entry.getKey());
-
-            if (dismissedByUserStats != null) {
-                try {
-                    mStatusBarService.onNotificationClear(
-                            entry.getSbn().getPackageName(),
-                            entry.getSbn().getTag(),
-                            entry.getSbn().getId(),
-                            entry.getSbn().getUser().getIdentifier(),
-                            entry.getSbn().getKey(),
-                            dismissedByUserStats.dismissalSurface,
-                            dismissedByUserStats.dismissalSentiment,
-                            dismissedByUserStats.notificationVisibility);
-                } catch (RemoteException e) {
-                    // system process is dead if we're here.
-                }
-            }
-
-            if (rankingMap != null) {
-                applyRanking(rankingMap);
-            }
-
-            dispatchOnEntryRemoved(entry, reason, dismissedByUserStats != null /* removedByUser */);
+            dispatchOnEntryRemoved(entry, entry.mCancellationReason);
             dispatchOnEntryCleanUp(entry);
+            return true;
+        } else {
+            return false;
         }
-
-        rebuildList();
     }
 
     private void applyRanking(@NonNull RankingMap rankingMap) {
         for (NotificationEntry entry : mNotificationSet.values()) {
-            if (!isLifetimeExtended(entry)) {
+            if (!isCanceled(entry)) {
 
                 // TODO: (b/148791039) We should crash if we are ever handed a ranking with
                 //  incomplete entries. Right now, there's a race condition in NotificationListener
@@ -355,9 +406,9 @@
         }
 
         if (!isLifetimeExtended(entry)) {
-            // TODO: This doesn't need to be undefined -- we can set either EXTENDER_EXPIRED or
-            // save the original reason
-            removeNotification(entry.getKey(), null, REASON_UNKNOWN, null);
+            if (tryRemoveNotification(entry)) {
+                rebuildList();
+            }
         }
     }
 
@@ -374,6 +425,31 @@
         return entry.mLifetimeExtenders.size() > 0;
     }
 
+    private void updateLifetimeExtension(NotificationEntry entry) {
+        entry.mLifetimeExtenders.clear();
+        mAmDispatchingToOtherCode = true;
+        for (NotifLifetimeExtender extender : mLifetimeExtenders) {
+            if (extender.shouldExtendLifetime(entry, entry.mCancellationReason)) {
+                entry.mLifetimeExtenders.add(extender);
+            }
+        }
+        mAmDispatchingToOtherCode = false;
+    }
+
+    private void cancelLocalDismissal(NotificationEntry entry) {
+        if (isDismissedByUser(entry)) {
+            entry.setDismissState(NOT_DISMISSED);
+            if (entry.getSbn().getNotification().isGroupSummary()) {
+                for (NotificationEntry otherEntry : mNotificationSet.values()) {
+                    if (otherEntry.getSbn().getGroupKey().equals(entry.getSbn().getGroupKey())
+                            && otherEntry.getDismissState() == PARENT_DISMISSED) {
+                        otherEntry.setDismissState(NOT_DISMISSED);
+                    }
+                }
+            }
+        }
+    }
+
     private void checkForReentrantCall() {
         if (mAmDispatchingToOtherCode) {
             throw new IllegalStateException("Reentrant call detected");
@@ -389,6 +465,19 @@
         return ranking;
     }
 
+    /**
+     * True if the notification has been canceled by system server. Usually, such notifications are
+     * immediately removed from the collection, but can sometimes stick around due to lifetime
+     * extenders.
+     */
+    private static boolean isCanceled(NotificationEntry entry) {
+        return entry.mCancellationReason != REASON_NOT_CANCELED;
+    }
+
+    private static boolean isDismissedByUser(NotificationEntry entry) {
+        return entry.getDismissState() != NOT_DISMISSED;
+    }
+
     private void dispatchOnEntryInit(NotificationEntry entry) {
         mAmDispatchingToOtherCode = true;
         for (NotifCollectionListener listener : mNotifCollectionListeners) {
@@ -421,13 +510,10 @@
         mAmDispatchingToOtherCode = false;
     }
 
-    private void dispatchOnEntryRemoved(
-            NotificationEntry entry,
-            @CancellationReason int reason,
-            boolean removedByUser) {
+    private void dispatchOnEntryRemoved(NotificationEntry entry, @CancellationReason int reason) {
         mAmDispatchingToOtherCode = true;
         for (NotifCollectionListener listener : mNotifCollectionListeners) {
-            listener.onEntryRemoved(entry, reason, removedByUser);
+            listener.onEntryRemoved(entry, reason);
         }
         mAmDispatchingToOtherCode = false;
     }
@@ -439,6 +525,20 @@
         }
         mAmDispatchingToOtherCode = false;
     }
+    @Override
+    public void dump(@NonNull FileDescriptor fd, PrintWriter pw, @NonNull String[] args) {
+        final List<NotificationEntry> entries = new ArrayList<>(getActiveNotifs());
+
+        pw.println("\t" + TAG + " unsorted/unfiltered notifications:");
+        if (entries.size() == 0) {
+            pw.println("\t\t None");
+        }
+        pw.println(
+                ListDumper.dumpList(
+                        entries,
+                        true,
+                        "\t\t"));
+    }
 
     private final BatchableNotificationHandler mNotifHandler = new BatchableNotificationHandler() {
         @Override
@@ -494,20 +594,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface CancellationReason {}
 
+    public static final int REASON_NOT_CANCELED = -1;
     public static final int REASON_UNKNOWN = 0;
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        final List<NotificationEntry> entries = new ArrayList<>(getActiveNotifs());
-
-        pw.println("\t" + TAG + " unsorted/unfiltered notifications:");
-        if (entries.size() == 0) {
-            pw.println("\t\t None");
-        }
-        pw.println(
-                ListDumper.dumpList(
-                        entries,
-                        true,
-                        "\t\t"));
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
index e7b772f..7a6d4f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -98,13 +98,12 @@
             @Override
             public void run() {
                 int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
-                /**
+                /*
                  * TODO: determine dismissal surface (ie: shade / headsup / aod)
                  * see {@link NotificationLogger#logNotificationClear}
                  */
                 mNotifCollection.dismissNotification(
                         entry,
-                        0,
                         new DismissedByUserStats(
                                 dismissalSurface,
                                 DISMISS_SENTIMENT_NEUTRAL,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 1f77ec2..df65dac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -22,6 +22,7 @@
 import static android.app.Notification.CATEGORY_MESSAGE;
 import static android.app.Notification.CATEGORY_REMINDER;
 import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
@@ -29,9 +30,11 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
 
+import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
 import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
 
-import android.annotation.NonNull;
+import static java.util.Objects.requireNonNull;
+
 import android.app.Notification;
 import android.app.Notification.MessagingStyle.Message;
 import android.app.NotificationChannel;
@@ -50,6 +53,7 @@
 import android.view.View;
 import android.widget.ImageView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -59,6 +63,7 @@
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
@@ -105,9 +110,18 @@
     /** If this was a group child that was promoted to the top level, then who did the promoting. */
     @Nullable NotifPromoter mNotifPromoter;
 
-    /** If this notification had an issue with inflating. Only used with the NewNotifPipeline **/
+    /**
+     * If this notification was cancelled by system server, then the reason that was supplied.
+     * Uncancelled notifications always have REASON_NOT_CANCELED. Note that lifetime-extended
+     * notifications will have this set even though they are still in the active notification set.
+     */
+    @CancellationReason int mCancellationReason = REASON_NOT_CANCELED;
+
+    /** @see #hasInflationError() */
     private boolean mHasInflationError;
 
+    /** @see #getDismissState() */
+    @NonNull private DismissState mDismissState = DismissState.NOT_DISMISSED;
 
     /*
     * Old members
@@ -164,18 +178,29 @@
     private Runnable mOnSensitiveChangedListener;
     private boolean mAutoHeadsUp;
     private boolean mPulseSupressed;
+    private boolean mAllowFgsDismissal;
     private int mBucket = BUCKET_ALERTING;
 
     public NotificationEntry(
             @NonNull StatusBarNotification sbn,
             @NonNull Ranking ranking) {
-        super(Objects.requireNonNull(Objects.requireNonNull(sbn).getKey()));
+        this(sbn, ranking, false);
+    }
 
-        Objects.requireNonNull(ranking);
+    public NotificationEntry(
+            @NonNull StatusBarNotification sbn,
+            @NonNull Ranking ranking,
+            boolean allowFgsDismissal
+    ) {
+        super(requireNonNull(Objects.requireNonNull(sbn).getKey()));
+
+        requireNonNull(ranking);
 
         mKey = sbn.getKey();
         setSbn(sbn);
         setRanking(ranking);
+
+        mAllowFgsDismissal = allowFgsDismissal;
     }
 
     @Override
@@ -201,8 +226,8 @@
      * TODO: Make this package-private
      */
     public void setSbn(@NonNull StatusBarNotification sbn) {
-        Objects.requireNonNull(sbn);
-        Objects.requireNonNull(sbn.getKey());
+        requireNonNull(sbn);
+        requireNonNull(sbn.getKey());
 
         if (!sbn.getKey().equals(mKey)) {
             throw new IllegalArgumentException("New key " + sbn.getKey()
@@ -227,8 +252,8 @@
      * TODO: Make this package-private
      */
     public void setRanking(@NonNull Ranking ranking) {
-        Objects.requireNonNull(ranking);
-        Objects.requireNonNull(ranking.getKey());
+        requireNonNull(ranking);
+        requireNonNull(ranking.getKey());
 
         if (!ranking.getKey().equals(mKey)) {
             throw new IllegalArgumentException("New key " + ranking.getKey()
@@ -239,6 +264,34 @@
     }
 
     /*
+     * Bookkeeping getters and setters
+     */
+
+    /**
+     * Whether this notification had an error when attempting to inflate. This is only used in
+     * the NewNotifPipeline
+     */
+    public boolean hasInflationError() {
+        return mHasInflationError;
+    }
+
+    void setHasInflationError(boolean hasError) {
+        mHasInflationError = hasError;
+    }
+
+    /**
+     * Set if the user has dismissed this notif but we haven't yet heard back from system server to
+     * confirm the dismissal.
+     */
+    @NonNull public DismissState getDismissState() {
+        return mDismissState;
+    }
+
+    void setDismissState(@NonNull DismissState dismissState) {
+        mDismissState = requireNonNull(dismissState);
+    }
+
+    /*
      * Convenience getters for SBN and Ranking members
      */
 
@@ -275,7 +328,6 @@
         return mRanking.canBubble();
     }
 
-
     public @NonNull List<Notification.Action> getSmartActions() {
         return mRanking.getSmartActions();
     }
@@ -578,18 +630,6 @@
         remoteInputTextWhenReset = null;
     }
 
-    void setHasInflationError(boolean hasError) {
-        mHasInflationError = hasError;
-    }
-
-    /**
-     * Whether this notification had an error when attempting to inflate. This is only used in
-     * the NewNotifPipeline
-     */
-    public boolean hasInflationError() {
-        return mHasInflationError;
-    }
-
     public void setHasSentReply() {
         hasSentReply = true;
     }
@@ -799,8 +839,11 @@
      *         notification can be dismissed in case notifications are sensitive on the lockscreen.
      * @see #canViewBeDismissed()
      */
+    // TOOD: This logic doesn't belong on NotificationEntry. It should be moved to the
+    // ForegroundsServiceDismissalFeatureController or some other controller that can be added
+    // as a dependency to any class that needs to answer this question.
     public boolean isClearable() {
-        if (!mSbn.isClearable()) {
+        if (!isDismissable()) {
             return false;
         }
 
@@ -808,7 +851,7 @@
         if (children != null && children.size() > 0) {
             for (int i = 0; i < children.size(); i++) {
                 NotificationEntry child =  children.get(i);
-                if (!child.isClearable()) {
+                if (!child.isDismissable()) {
                     return false;
                 }
             }
@@ -816,6 +859,31 @@
         return true;
     }
 
+    /**
+     * Notifications might have any combination of flags:
+     * - FLAG_ONGOING_EVENT
+     * - FLAG_NO_CLEAR
+     * - FLAG_FOREGROUND_SERVICE
+     *
+     * We want to allow dismissal of notifications that represent foreground services, which may
+     * have all 3 flags set. If we only find NO_CLEAR though, we don't want to allow dismissal
+     */
+    private boolean isDismissable() {
+        boolean ongoing = ((mSbn.getNotification().flags & Notification.FLAG_ONGOING_EVENT) != 0);
+        boolean noclear = ((mSbn.getNotification().flags & Notification.FLAG_NO_CLEAR) != 0);
+        boolean fgs = ((mSbn.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0);
+
+        if (mAllowFgsDismissal) {
+            if (noclear && !ongoing && !fgs) {
+                return false;
+            }
+            return true;
+        } else {
+            return mSbn.isClearable();
+        }
+
+    }
+
     public boolean canViewBeDismissed() {
         if (row == null) return true;
         return row.canViewBeDismissed();
@@ -974,6 +1042,16 @@
         }
     }
 
+    /** @see #getDismissState() */
+    public enum DismissState {
+        /** User has not dismissed this notif or its parent */
+        NOT_DISMISSED,
+        /** User has dismissed this notif specifically */
+        DISMISSED,
+        /** User has dismissed this notif's parent (which implicitly dismisses this one as well) */
+        PARENT_DISMISSED,
+    }
+
     private static final long LAUNCH_COOLDOWN = 2000;
     private static final long REMOTE_INPUT_COOLDOWN = 500;
     private static final long INITIALIZATION_DELAY = 400;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideLocallyDismissedNotifsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideLocallyDismissedNotifsCoordinator.java
new file mode 100644
index 0000000..0059e7b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideLocallyDismissedNotifsCoordinator.java
@@ -0,0 +1,41 @@
+/*
+ * 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.systemui.statusbar.notification.collection.coordinator;
+
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED;
+
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+/**
+ * Filters out notifications that have been dismissed locally (by the user) but that system server
+ * hasn't yet confirmed the removal of.
+ */
+public class HideLocallyDismissedNotifsCoordinator implements Coordinator {
+    @Override
+    public void attach(NotifPipeline pipeline) {
+        pipeline.addPreGroupFilter(mFilter);
+    }
+
+    private final NotifFilter mFilter = new NotifFilter("HideLocallyDismissedNotifsFilter") {
+        @Override
+        public boolean shouldFilterOut(NotificationEntry entry, long now) {
+            return entry.getDismissState() != NOT_DISMISSED;
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 8d0dd5b..0a1e09f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -56,6 +56,7 @@
             PreparationCoordinator preparationCoordinator) {
         dumpController.registerDumpable(TAG, this);
 
+        mCoordinators.add(new HideLocallyDismissedNotifsCoordinator());
         mCoordinators.add(keyguardCoordinator);
         mCoordinators.add(rankingCoordinator);
         mCoordinators.add(foregroundCoordinator);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 20c9cbc..41314b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -72,7 +72,7 @@
         }
 
         @Override
-        public void onEntryRemoved(NotificationEntry entry, int reason, boolean removedByUser) {
+        public void onEntryRemoved(NotificationEntry entry, int reason) {
             abortInflation(entry, "entryRemoved reason=" + reason);
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
index ff6da12..b2c53da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
@@ -54,10 +54,7 @@
      * immediately after a user dismisses a notification: we wait until we receive confirmation from
      * system server before considering the notification removed.
      */
-    default void onEntryRemoved(
-            NotificationEntry entry,
-            @CancellationReason int reason,
-            boolean removedByUser) {
+    default void onEntryRemoved(NotificationEntry entry, @CancellationReason int reason) {
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index 0d0a46a..14e1503 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -61,6 +61,14 @@
         })
     }
 
+    fun logNotifDismissed(key: String) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+        }, {
+            "DISMISSED $str1"
+        })
+    }
+
     fun logRankingMissing(key: String, rankingMap: RankingMap) {
         buffer.log(TAG, WARNING, { str1 = key }, { "Ranking update is missing ranking for $str1" })
         buffer.log(TAG, DEBUG, {}, { "Ranking map contents:" })
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
new file mode 100644
index 0000000..373457d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
@@ -0,0 +1,43 @@
+/*
+* 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.systemui.statusbar.notification.row
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.systemui.R
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+class DungeonRow(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
+    var entry: NotificationEntry? = null
+        set(value) {
+            field = value
+            update()
+        }
+
+    private fun update() {
+        (findViewById(R.id.app_name) as TextView).apply {
+            text = entry?.row?.appName
+        }
+
+        (findViewById(R.id.icon) as StatusBarIconView).apply {
+            set(entry?.icon?.statusBarIcon)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index b71beda..253be2fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -745,6 +745,11 @@
         mPrivateLayout.setRemoteInputController(r);
     }
 
+
+    String getAppName() {
+        return mAppName;
+    }
+
     public void addChildNotification(ExpandableNotificationRow row) {
         addChildNotification(row, -1);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt
new file mode 100644
index 0000000..17396ad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.systemui.statusbar.notification.row
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+
+import com.android.systemui.R
+
+class ForegroundServiceDungeonView(context: Context, attrs: AttributeSet)
+    : StackScrollerDecorView(context, attrs) {
+    override fun findContentView(): View? {
+        return findViewById(R.id.foreground_service_dungeon)
+    }
+
+    override fun findSecondaryView(): View? {
+        return null
+    }
+
+    override fun setVisible(visible: Boolean, animate: Boolean) {
+        // Visibility is controlled by the ForegroundServiceSectionController
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt
new file mode 100644
index 0000000..5757fe8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt
@@ -0,0 +1,171 @@
+/*
+ * 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.systemui.statusbar.notification.stack
+
+import android.content.Context
+import android.service.notification.NotificationListenerService.REASON_APP_CANCEL
+import android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL
+import android.service.notification.NotificationListenerService.REASON_CANCEL
+import android.service.notification.NotificationListenerService.REASON_CANCEL_ALL
+import android.service.notification.NotificationListenerService.REASON_CLICK
+import android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.LinearLayout
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController
+import com.android.systemui.statusbar.notification.NotificationEntryListener
+import com.android.systemui.statusbar.notification.NotificationEntryManager
+import com.android.systemui.statusbar.notification.row.DungeonRow
+import com.android.systemui.util.Assert
+
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * Controller for the bottom area of NotificationStackScrollLayout. It owns swiped-away foreground
+ * service notifications and can reinstantiate them when requested.
+ */
+@Singleton
+class ForegroundServiceSectionController @Inject constructor(
+    val entryManager: NotificationEntryManager,
+    val featureController: ForegroundServiceDismissalFeatureController
+) {
+    private val TAG = "FgsSectionController"
+    private var context: Context? = null
+
+    private val entries = mutableSetOf<NotificationEntry>()
+
+    private var entriesView: View? = null
+
+    init {
+        if (featureController.isForegroundServiceDismissalEnabled()) {
+            entryManager.addNotificationRemoveInterceptor(this::shouldInterceptRemoval)
+
+            entryManager.addNotificationEntryListener(object : NotificationEntryListener {
+                override fun onPostEntryUpdated(entry: NotificationEntry) {
+                    if (entries.contains(entry)) {
+                        removeEntry(entry)
+                        addEntry(entry)
+                        update()
+                    }
+                }
+            })
+        }
+    }
+
+    private fun shouldInterceptRemoval(
+        key: String,
+        entry: NotificationEntry?,
+        reason: Int
+    ): Boolean {
+        Assert.isMainThread()
+        val isClearAll = reason == REASON_CANCEL_ALL
+        val isUserDismiss = reason == REASON_CANCEL || reason == REASON_CLICK
+        val isAppCancel = reason == REASON_APP_CANCEL || reason == REASON_APP_CANCEL_ALL
+        val isSummaryCancel = reason == REASON_GROUP_SUMMARY_CANCELED
+
+        if (entry == null) return false
+
+        // We only want to retain notifications that the user dismissed
+        // TODO: centralize the entry.isClearable logic and this so that it's clear when a notif is
+        // clearable
+        if (isUserDismiss && !entry.sbn.isClearable) {
+            if (!hasEntry(entry)) {
+                addEntry(entry)
+                update()
+            }
+            // TODO: This isn't ideal. Slightly better would at least be to have NEM update the
+            // notif list when an entry gets intercepted
+            entryManager.updateNotifications(
+                    "FgsSectionController.onNotificationRemoveRequested")
+            return true
+        } else if ((isClearAll || isSummaryCancel) && !entry.sbn.isClearable) {
+            // In the case where a FGS notification is part of a group that is cleared or a clear
+            // all, we actually want to stop its removal but also not put it into the dungeon
+            return true
+        } else if (hasEntry(entry)) {
+            removeEntry(entry)
+            update()
+            return false
+        }
+
+        return false
+    }
+
+    private fun removeEntry(entry: NotificationEntry) {
+        Assert.isMainThread()
+        entries.remove(entry)
+    }
+
+    private fun addEntry(entry: NotificationEntry) {
+        Assert.isMainThread()
+        entries.add(entry)
+    }
+
+    fun hasEntry(entry: NotificationEntry): Boolean {
+        Assert.isMainThread()
+        return entries.contains(entry)
+    }
+
+    fun initialize(context: Context) {
+        this.context = context
+    }
+
+    fun createView(li: LayoutInflater): View {
+        entriesView = li.inflate(R.layout.foreground_service_dungeon, null)
+        // Start out gone
+        entriesView!!.visibility = View.GONE
+        return entriesView!!
+    }
+
+    private fun update() {
+        Assert.isMainThread()
+        if (entriesView == null) {
+            throw IllegalStateException("ForegroundServiceSectionController is trying to show " +
+                    "dismissed fgs notifications without having been initialized!")
+        }
+
+        // TODO: these views should be recycled and not inflating on the main thread
+        (entriesView!!.findViewById(R.id.entry_list) as LinearLayout).apply {
+            removeAllViews()
+            entries.sortedBy { it.ranking.rank }.forEach { entry ->
+                val child = LayoutInflater.from(context)
+                        .inflate(R.layout.foreground_service_dungeon_row, null) as DungeonRow
+
+                child.entry = entry
+                child.setOnClickListener {
+                    removeEntry(child.entry!!)
+                    update()
+                    entry.row.unDismiss()
+                    entry.row.resetTranslation()
+                    entryManager.updateNotifications("ForegroundServiceSectionController.onClick")
+                }
+
+                addView(child)
+            }
+        }
+
+        if (entries.isEmpty()) {
+            entriesView?.visibility = View.GONE
+        } else {
+            entriesView?.visibility = View.VISIBLE
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 84a293e..4b9976c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -35,7 +35,6 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -57,7 +56,6 @@
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Pair;
-import android.util.SparseArray;
 import android.view.ContextThemeWrapper;
 import android.view.InputDevice;
 import android.view.LayoutInflater;
@@ -109,6 +107,7 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.FakeShadowView;
+import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -121,6 +120,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.row.FooterView;
+import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -506,6 +506,8 @@
 
     private final NotificationGutsManager mNotificationGutsManager;
     private final NotificationSectionsManager mSectionsManager;
+    private final ForegroundServiceSectionController mFgsSectionController;
+    private ForegroundServiceDungeonView mFgsSectionView;
     private boolean mAnimateBottomOnLayout;
     private float mLastSentAppear;
     private float mLastSentExpandedHeight;
@@ -525,7 +527,10 @@
             NotificationLockscreenUserManager notificationLockscreenUserManager,
             NotificationGutsManager notificationGutsManager,
             ZenModeController zenController,
-            NotificationSectionsManager notificationSectionsManager) {
+            NotificationSectionsManager notificationSectionsManager,
+            ForegroundServiceSectionController fgsSectionController,
+            ForegroundServiceDismissalFeatureController fgsFeatureController
+    ) {
         super(context, attrs, 0, 0);
         Resources res = getResources();
 
@@ -541,6 +546,7 @@
         mKeyguardBypassController = keyguardBypassController;
         mFalsingManager = falsingManager;
         mZenController = zenController;
+        mFgsSectionController = fgsSectionController;
 
         mSectionsManager = notificationSectionsManager;
         mSectionsManager.initialize(this, LayoutInflater.from(context));
@@ -614,6 +620,17 @@
         dynamicPrivacyController.addListener(this);
         mDynamicPrivacyController = dynamicPrivacyController;
         mStatusbarStateController = statusBarStateController;
+        initializeForegroundServiceSection(fgsFeatureController);
+    }
+
+    private void initializeForegroundServiceSection(
+            ForegroundServiceDismissalFeatureController featureController) {
+        if (featureController.isForegroundServiceDismissalEnabled()) {
+            LayoutInflater li = LayoutInflater.from(mContext);
+            mFgsSectionView =
+                    (ForegroundServiceDungeonView) mFgsSectionController.createView(li);
+            addView(mFgsSectionView, -1);
+        }
     }
 
     private void updateDismissRtlSetting(boolean dismissRtl) {
@@ -3374,7 +3391,7 @@
         if (currentIndex == -1) {
             boolean isTransient = false;
             if (child instanceof ExpandableNotificationRow
-                    && ((ExpandableNotificationRow) child).getTransientContainer() != null) {
+                    && child.getTransientContainer() != null) {
                 isTransient = true;
             }
             Log.e(TAG, "Attempting to re-position "
@@ -3387,10 +3404,10 @@
 
         if (child != null && child.getParent() == this && currentIndex != newIndex) {
             mChangePositionInProgress = true;
-            ((ExpandableView) child).setChangingPosition(true);
+            child.setChangingPosition(true);
             removeView(child);
             addView(child, newIndex);
-            ((ExpandableView) child).setChangingPosition(false);
+            child.setChangingPosition(false);
             mChangePositionInProgress = false;
             if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
                 mChildrenChangingPositions.add(child);
@@ -5637,15 +5654,17 @@
      */
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void onUpdateRowStates() {
-        changeViewPosition(mFooterView, -1);
 
         // The following views will be moved to the end of mStackScroller. This counter represents
         // the offset from the last child. Initialized to 1 for the very last position. It is post-
         // incremented in the following "changeViewPosition" calls so that its value is correct for
         // subsequent calls.
         int offsetFromEnd = 1;
-        changeViewPosition(mEmptyShadeView,
-                getChildCount() - offsetFromEnd++);
+        if (mFgsSectionView != null) {
+            changeViewPosition(mFgsSectionView, getChildCount() - offsetFromEnd++);
+        }
+        changeViewPosition(mFooterView, getChildCount() - offsetFromEnd++);
+        changeViewPosition(mEmptyShadeView, getChildCount() - offsetFromEnd++);
 
         // No post-increment for this call because it is the last one. Make sure to add one if
         // another "changeViewPosition" call is ever added.
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
new file mode 100644
index 0000000..264ddc0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv;
+
+import com.android.systemui.dagger.SystemUIRootComponent;
+
+import dagger.Binds;
+import dagger.Module;
+
+@Module
+interface TvSystemUIBinder {
+    @Binds
+    SystemUIRootComponent bindSystemUIRootComponent(TvSystemUIRootComponent systemUIRootComponent);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java
new file mode 100644
index 0000000..7d3ec67
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv;
+
+import android.content.Context;
+
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.dagger.SystemUIRootComponent;
+
+/**
+ * TV variant {@link SystemUIFactory}, that substitutes default {@link SystemUIRootComponent} for
+ * {@link TvSystemUIRootComponent}
+ */
+public class TvSystemUIFactory extends SystemUIFactory {
+    @Override
+    protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
+        return DaggerTvSystemUIRootComponent.builder()
+                .context(context)
+                .build();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
new file mode 100644
index 0000000..fcf2700
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv;
+
+import android.content.Context;
+
+import com.android.systemui.dagger.DefaultComponentBinder;
+import com.android.systemui.dagger.DependencyBinder;
+import com.android.systemui.dagger.DependencyProvider;
+import com.android.systemui.dagger.SystemServicesModule;
+import com.android.systemui.dagger.SystemUIBinder;
+import com.android.systemui.dagger.SystemUIDefaultModule;
+import com.android.systemui.dagger.SystemUIModule;
+import com.android.systemui.dagger.SystemUIRootComponent;
+
+import javax.inject.Singleton;
+
+import dagger.BindsInstance;
+import dagger.Component;
+
+/**
+ * Root component for Dagger injection.
+ */
+@Singleton
+@Component(modules = {
+        DefaultComponentBinder.class,
+        DependencyProvider.class,
+        DependencyBinder.class,
+        SystemServicesModule.class,
+        SystemUIBinder.class,
+        SystemUIModule.class,
+        SystemUIDefaultModule.class,
+        TvSystemUIBinder.class})
+public interface TvSystemUIRootComponent extends SystemUIRootComponent {
+    /**
+     * Component Builder interface. This allows to bind Context instance in the component
+     */
+    @Component.Builder
+    interface Builder {
+        @BindsInstance Builder context(Context context);
+
+        TvSystemUIRootComponent build();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
index 264a683..64b0b66 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
@@ -31,9 +31,11 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.util.RotationUtils;
 import android.util.Size;
 import android.view.Display;
 import android.view.DisplayCutout;
@@ -43,8 +45,6 @@
 
 import com.android.internal.R;
 
-import java.util.List;
-
 /**
  * Contains information about the layout-properties of a display. This refers to internal layout
  * like insets/cutout/rotation. In general, this can be thought of as the System-UI analog to
@@ -323,28 +323,38 @@
         if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
             return null;
         }
+        final Insets waterfallInsets =
+                RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
         if (rotation == ROTATION_0) {
-            return computeSafeInsets(
-                    cutout, displayWidth, displayHeight);
+            return computeSafeInsets(cutout, displayWidth, displayHeight);
         }
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-        Rect[] cutoutRects = computeSafeInsets(cutout, displayWidth, displayHeight)
-                        .getBoundingRectsAll();
+        Rect[] cutoutRects = cutout.getBoundingRectsAll();
         final Rect[] newBounds = new Rect[cutoutRects.length];
         final Rect displayBounds = new Rect(0, 0, displayWidth, displayHeight);
         for (int i = 0; i < cutoutRects.length; ++i) {
-            newBounds[i] = new Rect(cutoutRects[i]);
-            rotateBounds(newBounds[i], displayBounds, rotation);
+            final Rect rect = new Rect(cutoutRects[i]);
+            if (!rect.isEmpty()) {
+                rotateBounds(rect, displayBounds, rotation);
+            }
+            newBounds[getBoundIndexFromRotation(i, rotation)] = rect;
         }
-        return computeSafeInsets(DisplayCutout.fromBounds(newBounds),
+        return computeSafeInsets(
+                DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets),
                 rotated ? displayHeight : displayWidth,
                 rotated ? displayWidth : displayHeight);
     }
 
+    private static int getBoundIndexFromRotation(int index, int rotation) {
+        return (index - rotation) < 0
+                ? index - rotation + DisplayCutout.BOUNDS_POSITION_LENGTH
+                : index - rotation;
+    }
+
     /** Calculate safe insets. */
     public static DisplayCutout computeSafeInsets(DisplayCutout inner,
             int displayWidth, int displayHeight) {
-        if (inner == DisplayCutout.NO_CUTOUT || inner.isBoundsEmpty()) {
+        if (inner == DisplayCutout.NO_CUTOUT) {
             return null;
         }
 
@@ -353,58 +363,44 @@
         return inner.replaceSafeInsets(safeInsets);
     }
 
-    private static Rect computeSafeInsets(Size displaySize, DisplayCutout cutout) {
-        if (displaySize.getWidth() < displaySize.getHeight()) {
-            final List<Rect> boundingRects = cutout.replaceSafeInsets(
-                    new Rect(0, displaySize.getHeight() / 2, 0, displaySize.getHeight() / 2))
-                    .getBoundingRects();
-            int topInset = findInsetForSide(displaySize, boundingRects, Gravity.TOP);
-            int bottomInset = findInsetForSide(displaySize, boundingRects, Gravity.BOTTOM);
-            return new Rect(0, topInset, 0, bottomInset);
-        } else if (displaySize.getWidth() > displaySize.getHeight()) {
-            final List<Rect> boundingRects = cutout.replaceSafeInsets(
-                    new Rect(displaySize.getWidth() / 2, 0, displaySize.getWidth() / 2, 0))
-                    .getBoundingRects();
-            int leftInset = findInsetForSide(displaySize, boundingRects, Gravity.LEFT);
-            int right = findInsetForSide(displaySize, boundingRects, Gravity.RIGHT);
-            return new Rect(leftInset, 0, right, 0);
-        } else {
+    private static Rect computeSafeInsets(
+            Size displaySize, DisplayCutout cutout) {
+        if (displaySize.getWidth() == displaySize.getHeight()) {
             throw new UnsupportedOperationException("not implemented: display=" + displaySize
                     + " cutout=" + cutout);
         }
+
+        int leftInset = Math.max(cutout.getWaterfallInsets().left,
+                findCutoutInsetForSide(displaySize, cutout.getBoundingRectLeft(), Gravity.LEFT));
+        int topInset = Math.max(cutout.getWaterfallInsets().top,
+                findCutoutInsetForSide(displaySize, cutout.getBoundingRectTop(), Gravity.TOP));
+        int rightInset = Math.max(cutout.getWaterfallInsets().right,
+                findCutoutInsetForSide(displaySize, cutout.getBoundingRectRight(), Gravity.RIGHT));
+        int bottomInset = Math.max(cutout.getWaterfallInsets().bottom,
+                findCutoutInsetForSide(displaySize, cutout.getBoundingRectBottom(),
+                        Gravity.BOTTOM));
+
+        return new Rect(leftInset, topInset, rightInset, bottomInset);
     }
 
-    private static int findInsetForSide(Size display, List<Rect> boundingRects, int gravity) {
-        int inset = 0;
-        final int size = boundingRects.size();
-        for (int i = 0; i < size; i++) {
-            Rect boundingRect = boundingRects.get(i);
-            switch (gravity) {
-                case Gravity.TOP:
-                    if (boundingRect.top == 0) {
-                        inset = Math.max(inset, boundingRect.bottom);
-                    }
-                    break;
-                case Gravity.BOTTOM:
-                    if (boundingRect.bottom == display.getHeight()) {
-                        inset = Math.max(inset, display.getHeight() - boundingRect.top);
-                    }
-                    break;
-                case Gravity.LEFT:
-                    if (boundingRect.left == 0) {
-                        inset = Math.max(inset, boundingRect.right);
-                    }
-                    break;
-                case Gravity.RIGHT:
-                    if (boundingRect.right == display.getWidth()) {
-                        inset = Math.max(inset, display.getWidth() - boundingRect.left);
-                    }
-                    break;
-                default:
-                    throw new IllegalArgumentException("unknown gravity: " + gravity);
-            }
+    private static int findCutoutInsetForSide(Size display, Rect boundingRect, int gravity) {
+        if (boundingRect.isEmpty()) {
+            return 0;
         }
-        return inset;
+
+        int inset = 0;
+        switch (gravity) {
+            case Gravity.TOP:
+                return Math.max(inset, boundingRect.bottom);
+            case Gravity.BOTTOM:
+                return Math.max(inset, display.getHeight() - boundingRect.top);
+            case Gravity.LEFT:
+                return Math.max(inset, boundingRect.right);
+            case Gravity.RIGHT:
+                return Math.max(inset, display.getWidth() - boundingRect.left);
+            default:
+                throw new IllegalArgumentException("unknown gravity: " + gravity);
+        }
     }
 
     static boolean hasNavigationBar(DisplayInfo info, Context context, int displayId) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 333b4a7..dcaf4ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -237,7 +237,7 @@
         mEntryListener = mEntryListenerCaptor.getValue();
         // And the remove interceptor
         verify(mNotificationEntryManager, atLeastOnce())
-                .setNotificationRemoveInterceptor(mRemoveInterceptorCaptor.capture());
+                .addNotificationRemoveInterceptor(mRemoveInterceptorCaptor.capture());
         mRemoveInterceptor = mRemoveInterceptorCaptor.getValue();
     }
 
@@ -581,7 +581,7 @@
 
         // Simulate notification cancellation.
         mRemoveInterceptor.onNotificationRemoveRequested(
-                mRow.getEntry().getKey(), REASON_APP_CANCEL);
+                mRow.getEntry().getKey(), mRow.getEntry(), REASON_APP_CANCEL);
 
         mBubbleController.expandStackAndSelectBubble(key);
     }
@@ -649,7 +649,7 @@
         assertTrue(mBubbleController.hasBubbles());
 
         boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
-                mRow.getEntry().getKey(), REASON_APP_CANCEL);
+                mRow.getEntry().getKey(), mRow.getEntry(), REASON_APP_CANCEL);
 
         // Cancels always remove so no need to intercept
         assertFalse(intercepted);
@@ -666,7 +666,7 @@
                 mRow.getEntry()));
 
         boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
-                mRow.getEntry().getKey(), REASON_CANCEL_ALL);
+                mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL_ALL);
 
         // Intercept!
         assertTrue(intercepted);
@@ -689,7 +689,7 @@
                 mRow.getEntry()));
 
         boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
-                mRow.getEntry().getKey(), REASON_CANCEL);
+                mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL);
 
         // Intercept!
         assertTrue(intercepted);
@@ -718,7 +718,7 @@
 
         // Dismiss the notification
         boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
-                mRow.getEntry().getKey(), REASON_CANCEL);
+                mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL);
 
         // It's no longer a bubble so we shouldn't intercept
         assertFalse(intercepted);
@@ -736,7 +736,8 @@
         assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
 
-        mRemoveInterceptor.onNotificationRemoveRequested(mRow.getEntry().getKey(), REASON_CANCEL);
+        mRemoveInterceptor.onNotificationRemoveRequested(
+                mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL);
 
         // Should update show in shade state
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index c97813d..63c911b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -49,6 +49,7 @@
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -104,7 +105,8 @@
                 mock(StatusBarStateControllerImpl.class), mEntryManager,
                 mock(KeyguardBypassController.class),
                 mock(BubbleController.class),
-                mock(DynamicPrivacyController.class));
+                mock(DynamicPrivacyController.class),
+                mock(ForegroundServiceSectionController.class));
         mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 296d0cef..20c67fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -247,11 +247,12 @@
                 mFeatureFlags,
                 () -> notificationRowBinder,
                 () -> mRemoteInputManager,
-                mLeakDetector
+                mLeakDetector,
+                mock(ForegroundServiceDismissalFeatureController.class)
         );
         mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
         mEntryManager.addNotificationEntryListener(mEntryListener);
-        mEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
+        mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor);
 
         notificationRowBinder.setUpWithPresenter(
                 mPresenter, mListContainer, mHeadsUpManager, mBindCallback);
@@ -546,7 +547,8 @@
         mEntryManager.addActiveNotificationForTest(mEntry);
 
         // GIVEN interceptor that intercepts that entry
-        when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.getKey()), anyInt()))
+        when(mRemoveInterceptor.onNotificationRemoveRequested(
+                eq(mEntry.getKey()), eq(mEntry), anyInt()))
                 .thenReturn(true);
 
         // WHEN the notification is removed
@@ -564,7 +566,8 @@
         mEntryManager.addActiveNotificationForTest(mEntry);
 
         // GIVEN interceptor that doesn't intercept
-        when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.getKey()), anyInt()))
+        when(mRemoveInterceptor.onNotificationRemoveRequested(
+                eq(mEntry.getKey()), eq(mEntry), anyInt()))
                 .thenReturn(false);
 
         // WHEN the notification is removed
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
index 29ce920..7431459 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
@@ -41,9 +41,10 @@
     ff: FeatureFlags,
     rb: dagger.Lazy<NotificationRowBinder>,
     notificationRemoteInputManagerLazy: dagger.Lazy<NotificationRemoteInputManager>,
-    leakDetector: LeakDetector
+    leakDetector: LeakDetector,
+    fgsFeatureController: ForegroundServiceDismissalFeatureController
 ) : NotificationEntryManager(log, gm, rm, ke, ff, rb,
-        notificationRemoteInputManagerLazy, leakDetector) {
+        notificationRemoteInputManagerLazy, leakDetector, fgsFeatureController) {
 
     public var countDownLatch: CountDownLatch = CountDownLatch(1)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index b652980..7c94ed2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -17,9 +17,16 @@
 package com.android.systemui.statusbar.notification.collection;
 
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_CLICK;
+import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
 
+import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
 import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.DISMISSED;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -40,7 +47,6 @@
 import android.annotation.Nullable;
 import android.os.RemoteException;
 import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationStats;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.ArrayMap;
@@ -77,6 +83,7 @@
 
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -236,7 +243,7 @@
         mNoMan.retractNotif(notif.sbn, REASON_APP_CANCEL);
 
         // THEN the listener is notified
-        verify(mCollectionListener).onEntryRemoved(entry, REASON_APP_CANCEL, false);
+        verify(mCollectionListener).onEntryRemoved(entry, REASON_APP_CANCEL);
         verify(mCollectionListener).onEntryCleanUp(entry);
         assertEquals(notif.sbn, entry.getSbn());
         assertEquals(notif.ranking, entry.getRanking());
@@ -322,24 +329,15 @@
     }
 
     @Test
-    public void testDismissNotification() throws RemoteException {
-        // GIVEN a collection with a couple notifications and a lifetime extender
-        mCollection.addNotificationLifetimeExtender(mExtender1);
-
+    public void testDismissNotificationSentToSystemServer() throws RemoteException {
+        // GIVEN a collection with a couple notifications
         NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
         NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88, "barTag"));
         NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
 
         // WHEN a notification is manually dismissed
-        DismissedByUserStats stats = new DismissedByUserStats(
-                NotificationStats.DISMISSAL_SHADE,
-                NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
-                NotificationVisibility.obtain(entry2.getKey(), 7, 2, true));
-
-        mCollection.dismissNotification(entry2, REASON_CLICK, stats);
-
-        // THEN we check for lifetime extension
-        verify(mExtender1).shouldExtendLifetime(entry2, REASON_CLICK);
+        DismissedByUserStats stats = defaultStats(entry2);
+        mCollection.dismissNotification(entry2, defaultStats(entry2));
 
         // THEN we send the dismissal to system server
         verify(mStatusBarService).onNotificationClear(
@@ -351,9 +349,96 @@
                 stats.dismissalSurface,
                 stats.dismissalSentiment,
                 stats.notificationVisibility);
+    }
 
-        // THEN we fire a remove event
-        verify(mCollectionListener).onEntryRemoved(entry2, REASON_CLICK, true);
+    @Test
+    public void testDismissedNotificationsAreMarkedAsDismissedLocally() {
+        // GIVEN a collection with a notification
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+        NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+        // WHEN a notification is manually dismissed
+        mCollection.dismissNotification(entry1, defaultStats(entry1));
+
+        // THEN the entry is marked as dismissed locally
+        assertEquals(DISMISSED, entry1.getDismissState());
+    }
+
+    @Test
+    public void testDismissedNotificationsCannotBeLifetimeExtended() {
+        // GIVEN a collection with a notification and a lifetime extender
+        mCollection.addNotificationLifetimeExtender(mExtender1);
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+        NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+        // WHEN a notification is manually dismissed
+        mCollection.dismissNotification(entry1, defaultStats(entry1));
+
+        // THEN lifetime extenders are never queried
+        verify(mExtender1, never()).shouldExtendLifetime(eq(entry1), anyInt());
+    }
+
+    @Test
+    public void testDismissedNotificationsDoNotTriggerRemovalEvents() {
+        // GIVEN a collection with a notification
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+        NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+        // WHEN a notification is manually dismissed
+        mCollection.dismissNotification(entry1, defaultStats(entry1));
+
+        // THEN onEntryRemoved is not called
+        verify(mCollectionListener, never()).onEntryRemoved(eq(entry1), anyInt());
+    }
+
+    @Test
+    public void testDismissedNotificationsStillAppearInNotificationSet() {
+        // GIVEN a collection with a notification
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+        NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+        // WHEN a notification is manually dismissed
+        mCollection.dismissNotification(entry1, defaultStats(entry1));
+
+        // THEN the dismissed entry still appears in the notification set
+        assertEquals(
+                new ArraySet<>(Collections.singletonList(entry1)),
+                new ArraySet<>(mCollection.getActiveNotifs()));
+    }
+
+    @Test
+    public void testDismissingLifetimeExtendedSummaryDoesNotDismissChildren() {
+        // GIVEN A notif group with one summary and two children
+        mCollection.addNotificationLifetimeExtender(mExtender1);
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 1, "myTag")
+                .setGroup(mContext, GROUP_1)
+                .setGroupSummary(mContext, true));
+        NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 2, "myTag")
+                .setGroup(mContext, GROUP_1));
+        NotifEvent notif3 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3, "myTag")
+                .setGroup(mContext, GROUP_1));
+
+        NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+        NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+        NotificationEntry entry3 = mCollectionListener.getEntry(notif3.key);
+
+        // GIVEN that the summary and one child are retracted, but both are lifetime-extended
+        mExtender1.shouldExtendLifetime = true;
+        mNoMan.retractNotif(notif1.sbn, REASON_CANCEL);
+        mNoMan.retractNotif(notif2.sbn, REASON_CANCEL);
+        assertEquals(
+                new ArraySet<>(List.of(entry1, entry2, entry3)),
+                new ArraySet<>(mCollection.getActiveNotifs()));
+
+        // WHEN the summary is dismissed by the user
+        mCollection.dismissNotification(entry1, defaultStats(entry1));
+
+        // THEN the summary is removed, but both children stick around
+        assertEquals(
+                new ArraySet<>(List.of(entry2, entry3)),
+                new ArraySet<>(mCollection.getActiveNotifs()));
+        assertEquals(NOT_DISMISSED, entry2.getDismissState());
+        assertEquals(NOT_DISMISSED, entry3.getDismissState());
     }
 
     @Test(expected = IllegalStateException.class)
@@ -366,15 +451,115 @@
         mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
 
         // WHEN we try to dismiss a notification that isn't present
-        mCollection.dismissNotification(
-                entry2,
-                REASON_CLICK,
-                new DismissedByUserStats(0, 0, NotificationVisibility.obtain("foo", 47, 3, true)));
+        mCollection.dismissNotification(entry2, defaultStats(entry2));
 
         // THEN an exception is thrown
     }
 
     @Test
+    public void testGroupChildrenAreDismissedLocallyWhenSummaryIsDismissed() {
+        // GIVEN a collection with two grouped notifs in it
+        NotifEvent notif0 = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 0)
+                        .setGroup(mContext, GROUP_1)
+                        .setGroupSummary(mContext, true));
+        NotifEvent notif1 = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 1)
+                        .setGroup(mContext, GROUP_1));
+        NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
+        NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+        // WHEN the summary is dismissed
+        mCollection.dismissNotification(entry0, defaultStats(entry0));
+
+        // THEN all members of the group are marked as dismissed locally
+        assertEquals(DISMISSED, entry0.getDismissState());
+        assertEquals(PARENT_DISMISSED, entry1.getDismissState());
+    }
+
+    @Test
+    public void testUpdatingDismissedSummaryBringsChildrenBack() {
+        // GIVEN a collection with two grouped notifs in it
+        NotifEvent notif0 = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 0)
+                        .setGroup(mContext, GROUP_1)
+                        .setGroupSummary(mContext, true));
+        NotifEvent notif1 = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 1)
+                        .setGroup(mContext, GROUP_1));
+        NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
+        NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+        // WHEN the summary is dismissed but then reposted without a group
+        mCollection.dismissNotification(entry0, defaultStats(entry0));
+        NotifEvent notif0a = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 0));
+
+        // THEN it and all of its previous children are no longer dismissed locally
+        assertEquals(NOT_DISMISSED, entry0.getDismissState());
+        assertEquals(NOT_DISMISSED, entry1.getDismissState());
+    }
+
+    @Test
+    public void testDismissedChildrenAreNotResetByParentUpdate() {
+        // GIVEN a collection with three grouped notifs in it
+        NotifEvent notif0 = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 0)
+                        .setGroup(mContext, GROUP_1)
+                        .setGroupSummary(mContext, true));
+        NotifEvent notif1 = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 1)
+                        .setGroup(mContext, GROUP_1));
+        NotifEvent notif2 = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 2)
+                        .setGroup(mContext, GROUP_1));
+        NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
+        NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+        NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+
+        // WHEN a child is dismissed, then the parent is dismissed, then the parent is updated
+        mCollection.dismissNotification(entry1, defaultStats(entry1));
+        mCollection.dismissNotification(entry0, defaultStats(entry0));
+        NotifEvent notif0a = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 0));
+
+        // THEN the manually-dismissed child is still marked as dismissed
+        assertEquals(NOT_DISMISSED, entry0.getDismissState());
+        assertEquals(DISMISSED, entry1.getDismissState());
+        assertEquals(NOT_DISMISSED, entry2.getDismissState());
+    }
+
+    @Test
+    public void testUpdatingGroupKeyOfDismissedSummaryBringsChildrenBack() {
+        // GIVEN a collection with two grouped notifs in it
+        NotifEvent notif0 = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 0)
+                        .setOverrideGroupKey(GROUP_1)
+                        .setGroupSummary(mContext, true));
+        NotifEvent notif1 = mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 1)
+                        .setOverrideGroupKey(GROUP_1));
+        NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
+        NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+        // WHEN the summary is dismissed but then reposted AND in the same update one of the
+        // children's ranking loses its override group
+        mCollection.dismissNotification(entry0, defaultStats(entry0));
+        mNoMan.setRanking(entry1.getKey(), new RankingBuilder()
+                .setKey(entry1.getKey())
+                .build());
+        mNoMan.postNotif(
+                buildNotif(TEST_PACKAGE, 0)
+                        .setOverrideGroupKey(GROUP_1)
+                        .setGroupSummary(mContext, true));
+
+        // THEN it and all of its previous children are no longer dismissed locally, including the
+        // child that is no longer part of the group
+        assertEquals(NOT_DISMISSED, entry0.getDismissState());
+        assertEquals(NOT_DISMISSED, entry1.getDismissState());
+    }
+
+    @Test
     public void testLifetimeExtendersAreQueriedWhenNotifRemoved() {
         // GIVEN a couple notifications and a few lifetime extenders
         mExtender1.shouldExtendLifetime = true;
@@ -389,12 +574,12 @@
         NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
 
         // WHEN a notification is removed
-        mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
+        mNoMan.retractNotif(notif2.sbn, REASON_CLICK);
 
         // THEN each extender is asked whether to extend, even if earlier ones return true
-        verify(mExtender1).shouldExtendLifetime(entry2, REASON_UNKNOWN);
-        verify(mExtender2).shouldExtendLifetime(entry2, REASON_UNKNOWN);
-        verify(mExtender3).shouldExtendLifetime(entry2, REASON_UNKNOWN);
+        verify(mExtender1).shouldExtendLifetime(entry2, REASON_CLICK);
+        verify(mExtender2).shouldExtendLifetime(entry2, REASON_CLICK);
+        verify(mExtender3).shouldExtendLifetime(entry2, REASON_CLICK);
 
         // THEN the entry is not removed
         assertTrue(mCollection.getActiveNotifs().contains(entry2));
@@ -428,9 +613,9 @@
         mExtender2.callback.onEndLifetimeExtension(mExtender2, entry2);
 
         // THEN each extender is re-queried
-        verify(mExtender1).shouldExtendLifetime(entry2, REASON_UNKNOWN);
-        verify(mExtender2).shouldExtendLifetime(entry2, REASON_UNKNOWN);
-        verify(mExtender3).shouldExtendLifetime(entry2, REASON_UNKNOWN);
+        verify(mExtender1).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender2).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender3).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
 
         // THEN the entry is not removed
         assertTrue(mCollection.getActiveNotifs().contains(entry2));
@@ -466,9 +651,9 @@
         assertTrue(mCollection.getActiveNotifs().contains(entry2));
 
         // THEN we don't re-query the extenders
-        verify(mExtender1, never()).shouldExtendLifetime(eq(entry2), anyInt());
-        verify(mExtender2, never()).shouldExtendLifetime(eq(entry2), anyInt());
-        verify(mExtender3, never()).shouldExtendLifetime(eq(entry2), anyInt());
+        verify(mExtender1, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender2, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender3, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
 
         // THEN the entry properly records all extenders that returned true
         assertEquals(Arrays.asList(mExtender1), entry2.mLifetimeExtenders);
@@ -501,7 +686,7 @@
 
         // THEN the entry removed
         assertFalse(mCollection.getActiveNotifs().contains(entry2));
-        verify(mCollectionListener).onEntryRemoved(entry2, REASON_UNKNOWN, false);
+        verify(mCollectionListener).onEntryRemoved(entry2, REASON_UNKNOWN);
     }
 
     @Test
@@ -591,6 +776,36 @@
         assertEquals(notif2a.ranking, entry2.getRanking());
     }
 
+    @Test
+    public void testCancellationReasonIsSetWhenNotifIsCancelled() {
+        // GIVEN a notification
+        NotifEvent notif0 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3));
+        NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
+
+        // WHEN the notification is retracted
+        mNoMan.retractNotif(notif0.sbn, REASON_APP_CANCEL);
+
+        // THEN the retraction reason is stored on the notif
+        assertEquals(REASON_APP_CANCEL, entry0.mCancellationReason);
+    }
+
+    @Test
+    public void testCancellationReasonIsClearedWhenNotifIsUpdated() {
+        // GIVEN a notification and a lifetime extender that will preserve it
+        NotifEvent notif0 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3));
+        NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
+        mCollection.addNotificationLifetimeExtender(mExtender1);
+        mExtender1.shouldExtendLifetime = true;
+
+        // WHEN the notification is retracted and subsequently reposted
+        mNoMan.retractNotif(notif0.sbn, REASON_APP_CANCEL);
+        assertEquals(REASON_APP_CANCEL, entry0.mCancellationReason);
+        mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3));
+
+        // THEN the notification has its cancellation reason cleared
+        assertEquals(REASON_NOT_CANCELED, entry0.mCancellationReason);
+    }
+
     private static NotificationEntryBuilder buildNotif(String pkg, int id, String tag) {
         return new NotificationEntryBuilder()
                 .setPkg(pkg)
@@ -604,6 +819,13 @@
                 .setId(id);
     }
 
+    private static DismissedByUserStats defaultStats(NotificationEntry entry) {
+        return new DismissedByUserStats(
+                DISMISSAL_SHADE,
+                DISMISS_SENTIMENT_NEUTRAL,
+                NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
+    }
+
     private static class RecordingCollectionListener implements NotifCollectionListener {
         private final Map<String, NotificationEntry> mLastSeenEntries = new ArrayMap<>();
 
@@ -621,7 +843,7 @@
         }
 
         @Override
-        public void onEntryRemoved(NotificationEntry entry, int reason, boolean removedByUser) {
+        public void onEntryRemoved(NotificationEntry entry, int reason) {
         }
 
         @Override
@@ -674,4 +896,6 @@
 
     private static final String TEST_PACKAGE = "com.android.test.collection";
     private static final String TEST_PACKAGE2 = "com.android.test.collection2";
+
+    private static final String GROUP_1 = "group_1";
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 5bd5638..70d76f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -63,6 +63,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
@@ -178,7 +179,9 @@
                 mock(FeatureFlags.class),
                 () -> mock(NotificationRowBinder.class),
                 () -> mRemoteInputManager,
-                mock(LeakDetector.class));
+                mock(LeakDetector.class),
+                mock(ForegroundServiceDismissalFeatureController.class)
+        );
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager);
 
@@ -203,7 +206,10 @@
                 mLockscreenUserManager,
                 mock(NotificationGutsManager.class),
                 mZenModeController,
-                mNotificationSectionsManager);
+                mNotificationSectionsManager,
+                mock(ForegroundServiceSectionController.class),
+                mock(ForegroundServiceDismissalFeatureController.class)
+        );
         verify(mLockscreenUserManager).addUserChangedListener(userChangedCaptor.capture());
         mUserChangedListener = userChangedCaptor.getValue();
         mStackScroller = spy(mStackScrollerInternal);
@@ -394,8 +400,11 @@
 
         mStackScroller.onUpdateRowStates();
 
+        // Expecting the footer to be the last child
+        int expected = mStackScroller.getChildCount() - 1;
+
         // move footer to end
-        verify(mStackScroller).changeViewPosition(any(FooterView.class), eq(-1 /* end */));
+        verify(mStackScroller).changeViewPosition(any(FooterView.class), eq(expected));
     }
 
     @Test
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index c489cbc..4af5c53 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -47,6 +47,7 @@
          should be empty.  An example would be "p2p-p2p\\d-.*" -->
     <string-array translatable="false" name="config_tether_wifi_p2p_regexs">
         <item>"p2p-p2p\\d-.*"</item>
+        <item>"p2p\\d"</item>
     </string-array>
 
     <!-- List of regexpressions describing the interface (if any) that represent tetherable
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
index 8a3ac94..2bb673e 100644
--- a/packages/overlays/Android.mk
+++ b/packages/overlays/Android.mk
@@ -26,6 +26,7 @@
 	AccentColorPurpleOverlay \
 	DisplayCutoutEmulationCornerOverlay \
 	DisplayCutoutEmulationDoubleOverlay \
+        DisplayCutoutEmulationHoleOverlay \
 	DisplayCutoutEmulationTallOverlay \
 	FontNotoSerifSourceOverlay \
 	IconPackCircularAndroidOverlay \
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.mk
new file mode 100644
index 0000000..6d8fc24
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := DisplayCutoutEmulationHole
+
+
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := DisplayCutoutEmulationHoleOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..9fd82ab
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.internal.display.cutout.emulation.hole"
+        android:versionCode="1"
+        android:versionName="1.0">
+    <overlay android:targetPackage="android"
+            android:category="com.android.internal.display_cutout_emulation"
+            android:priority="1"/>
+
+    <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-land/config.xml
new file mode 100644
index 0000000..2e971de
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-land/config.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ 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
+  -->
+
+<resources>
+    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
+    <dimen name="quick_qs_offset_height">28dp</dimen>
+    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
+    <dimen name="quick_qs_total_height">156dp</dimen>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml
new file mode 100644
index 0000000..9f558d0
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml
@@ -0,0 +1,61 @@
+<!--
+  ~ 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.
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- The bounding path of the cutout region of the main built-in display.
+         Must either be empty if there is no cutout region, or a string that is parsable by
+         {@link android.util.PathParser}.
+
+         The path is assumed to be specified in display coordinates with pixel units and in
+         the display's native orientation, with the origin of the coordinate system at the
+         center top of the display.
+
+         To facilitate writing device-independent emulation overlays, the marker `@dp` can be
+         appended after the path string to interpret coordinates in dp instead of px units.
+         Note that a physical cutout should be configured in pixels for the best results.
+         -->
+    <!-- Display cutout configuration -->
+    <string translatable="false" name="config_mainBuiltInDisplayCutout">
+        M 128,83 A 44,44 0 0 1 84,127 44,44 0 0 1 40,83 44,44 0 0 1 84,39 44,44 0 0 1 128,83 Z
+        @left
+    </string>
+
+    <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">
+        M 0.0,0.0
+        h 136
+        v 136
+        h -136
+        Z
+        @left
+    </string>
+
+    <!-- Whether the display cutout region of the main built-in display should be forced to
+         black in software (to avoid aliasing or emulate a cutout that is not physically existent).
+     -->
+    <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
+
+    <!-- Height of the status bar -->
+    <dimen name="status_bar_height_portrait">136px</dimen>
+    <dimen name="status_bar_height_landscape">28dp</dimen>
+    <!-- Height of area above QQS where battery/time go (equal to status bar) -->
+    <dimen name="quick_qs_offset_height">136px</dimen>
+    <!-- Total height of QQS (quick_qs_offset_height + 128) -->
+    <dimen name="quick_qs_total_height">488px</dimen>
+
+</resources>
+
+
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/strings.xml
new file mode 100644
index 0000000..ab25695e
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <string name="display_cutout_emulation_overlay">Punch Hole cutout</string>
+
+</resources>
+
diff --git a/packages/overlays/IconShapePebbleOverlay/Android.mk b/packages/overlays/IconShapePebbleOverlay/Android.mk
new file mode 100644
index 0000000..c163bb9
--- /dev/null
+++ b/packages/overlays/IconShapePebbleOverlay/Android.mk
@@ -0,0 +1,29 @@
+#
+#  Copyright 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconShapePebble
+
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconShapePebbleOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconShapePebbleOverlay/AndroidManifest.xml b/packages/overlays/IconShapePebbleOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..6842dde
--- /dev/null
+++ b/packages/overlays/IconShapePebbleOverlay/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.theme.icon.pebble"
+    android:versionCode="1"
+    android:versionName="1.0">
+<overlay
+        android:targetPackage="android"
+        android:targetName="IconShapeCustomization"
+        android:category="android.theme.customization.adaptive_icon_shape"
+        android:priority="1"/>
+
+    <application android:label="@string/icon_shape_pebble_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconShapePebbleOverlay/res/values/config.xml b/packages/overlays/IconShapePebbleOverlay/res/values/config.xml
new file mode 100644
index 0000000..2465fe0
--- /dev/null
+++ b/packages/overlays/IconShapePebbleOverlay/res/values/config.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+    <string name="config_icon_mask" translatable="false">"MM55,0 C25,0 0,25 0,50 0,78 28,100 55,100 85,100 100,85 100,58 100,30 86,0 55,0 Z"</string>
+    <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+    <bool name="config_useRoundIcon">false</bool>
+    <!-- Corner radius of system dialogs -->
+    <dimen name="config_dialogCornerRadius">8dp</dimen>
+    <!-- Corner radius for bottom sheet system dialogs -->
+    <dimen name="config_bottomDialogCornerRadius">16dp</dimen>
+
+</resources>
+
diff --git a/packages/overlays/IconShapePebbleOverlay/res/values/strings.xml b/packages/overlays/IconShapePebbleOverlay/res/values/strings.xml
new file mode 100644
index 0000000..aec4a82
--- /dev/null
+++ b/packages/overlays/IconShapePebbleOverlay/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Heart icon overlay -->
+    <string name="icon_shape_pebble_overlay" translatable="false">Pebble</string>
+
+</resources>
diff --git a/services/Android.bp b/services/Android.bp
index a582453..28c8aee 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -114,7 +114,7 @@
     srcs: [":services-all-sources"],
     installable: false,
     // TODO: remove the --hide options below
-    args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES,process=android.annotation.SystemApi.Process.SYSTEM_SERVER\\)" +
+    args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)" +
         " --hide-annotation android.annotation.Hide" +
         " --hide-package com.google.android.startop.iorap" +
         " --hide ReferencesHidden" +
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 4f49fb7..48d976b 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -32,11 +32,14 @@
 import android.os.RemoteException;
 import android.service.appprediction.AppPredictionService;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.infra.AbstractPerUserSystemService;
 
+import java.util.Map;
+import java.util.Set;
 import java.util.function.Consumer;
 
 /**
@@ -48,9 +51,13 @@
 
     private static final String TAG = AppPredictionPerUserService.class.getSimpleName();
 
-    @Nullable
+    /**
+     * A lookup of remote services in respect to their {@link ComponentName}.
+     */
+    @NonNull
     @GuardedBy("mLock")
-    private RemoteAppPredictionService mRemoteService;
+    private final ArrayMap<ComponentName, RemoteAppPredictionService> mRemoteServices =
+            new ArrayMap<>();
 
     /**
      * When {@code true}, remote service died but service state is kept so it's restored after
@@ -92,7 +99,7 @@
         if (enabledChanged) {
             if (!isEnabledLocked()) {
                 // Clear the remote service for the next call
-                mRemoteService = null;
+                mRemoteServices.clear();
             }
         }
         return enabledChanged;
@@ -104,14 +111,14 @@
     @GuardedBy("mLock")
     public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context,
             @NonNull AppPredictionSessionId sessionId) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        if (!mSessionInfos.containsKey(sessionId)) {
+            mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
+                    resolveComponentName(context), this::removeAppPredictionSessionInfo));
+        }
+        final RemoteAppPredictionService service = getRemoteServiceLocked(
+                getComponentName(sessionId));
         if (service != null) {
             service.onCreatePredictionSession(context, sessionId);
-
-            if (!mSessionInfos.containsKey(sessionId)) {
-                mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
-                        this::removeAppPredictionSessionInfo));
-            }
         }
     }
 
@@ -121,7 +128,8 @@
     @GuardedBy("mLock")
     public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull AppTargetEvent event) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        final RemoteAppPredictionService service = getRemoteServiceLocked(
+                getComponentName(sessionId));
         if (service != null) {
             service.notifyAppTargetEvent(sessionId, event);
         }
@@ -133,7 +141,8 @@
     @GuardedBy("mLock")
     public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        final RemoteAppPredictionService service = getRemoteServiceLocked(
+                getComponentName(sessionId));
         if (service != null) {
             service.notifyLaunchLocationShown(sessionId, launchLocation, targetIds);
         }
@@ -145,7 +154,8 @@
     @GuardedBy("mLock")
     public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        final RemoteAppPredictionService service = getRemoteServiceLocked(
+                getComponentName(sessionId));
         if (service != null) {
             service.sortAppTargets(sessionId, targets, callback);
         }
@@ -157,7 +167,8 @@
     @GuardedBy("mLock")
     public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull IPredictionCallback callback) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        final RemoteAppPredictionService service = getRemoteServiceLocked(
+                getComponentName(sessionId));
         if (service != null) {
             service.registerPredictionUpdates(sessionId, callback);
 
@@ -174,7 +185,8 @@
     @GuardedBy("mLock")
     public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull IPredictionCallback callback) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        final RemoteAppPredictionService service = getRemoteServiceLocked(
+                getComponentName(sessionId));
         if (service != null) {
             service.unregisterPredictionUpdates(sessionId, callback);
 
@@ -190,7 +202,8 @@
      */
     @GuardedBy("mLock")
     public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        final RemoteAppPredictionService service = getRemoteServiceLocked(
+                getComponentName(sessionId));
         if (service != null) {
             service.requestPredictionUpdate(sessionId);
         }
@@ -201,7 +214,8 @@
      */
     @GuardedBy("mLock")
     public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
+        final RemoteAppPredictionService service = getRemoteServiceLocked(
+                getComponentName(sessionId));
         if (service != null) {
             service.onDestroyPredictionSession(sessionId);
 
@@ -229,7 +243,7 @@
             synchronized (mLock) {
                 if (mZombie) {
                     // Sanity check - shouldn't happen
-                    if (mRemoteService == null) {
+                    if (mRemoteServices.isEmpty()) {
                         Slog.w(TAG, "Cannot resurrect sessions because remote service is null");
                         return;
                     }
@@ -266,22 +280,30 @@
     }
 
     private void destroyAndRebindRemoteService() {
-        if (mRemoteService == null) {
+        if (mRemoteServices.isEmpty()) {
             return;
         }
 
         if (isDebug()) {
             Slog.d(TAG, "Destroying the old remote service.");
         }
-        mRemoteService.destroy();
-        mRemoteService = null;
+        final Set<Map.Entry<ComponentName, RemoteAppPredictionService>> services =
+                new ArraySet<>(mRemoteServices.entrySet());
+        mRemoteServices.clear();
+        services.stream().forEach(entry -> destroyAndRebindRemoteService(
+                entry.getKey(), entry.getValue()));
+    }
 
-        mRemoteService = getRemoteServiceLocked();
-        if (mRemoteService != null) {
+    private void destroyAndRebindRemoteService(
+            @NonNull final ComponentName component,
+            @NonNull final RemoteAppPredictionService service) {
+        service.destroy();
+        final RemoteAppPredictionService newService = getRemoteServiceLocked(component);
+        if (newService != null) {
             if (isDebug()) {
                 Slog.d(TAG, "Rebinding to the new remote service.");
             }
-            mRemoteService.reconnect();
+            newService.reconnect();
         }
     }
 
@@ -292,7 +314,7 @@
     private void resurrectSessionsLocked() {
         final int numSessions = mSessionInfos.size();
         if (isDebug()) {
-            Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on "
+            Slog.d(TAG, "Resurrecting remote service (" + mRemoteServices + ") on "
                     + numSessions + " sessions.");
         }
 
@@ -310,32 +332,49 @@
         }
     }
 
+    @Nullable
+    private ComponentName resolveComponentName(@NonNull final AppPredictionContext context) {
+        // TODO: add logic to determine serviceName based on context
+        final String serviceName = getComponentNameLocked();
+        if (serviceName == null) {
+            if (mMaster.verbose) {
+                Slog.v(TAG, "getRemoteServiceLocked(): not set, context = " + context);
+            }
+            return null;
+        }
+        return ComponentName.unflattenFromString(serviceName);
+    }
+
+    @Nullable
+    private ComponentName getComponentName(@NonNull final AppPredictionSessionId sessionId) {
+        AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        return sessionInfo == null ? null : sessionInfo.mComponentName;
+    }
+
     @GuardedBy("mLock")
     @Nullable
-    private RemoteAppPredictionService getRemoteServiceLocked() {
-        if (mRemoteService == null) {
-            final String serviceName = getComponentNameLocked();
-            if (serviceName == null) {
-                if (mMaster.verbose) {
-                    Slog.v(TAG, "getRemoteServiceLocked(): not set");
-                }
-                return null;
-            }
-            ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
-
-            mRemoteService = new RemoteAppPredictionService(getContext(),
+    private RemoteAppPredictionService getRemoteServiceLocked(
+            @Nullable final ComponentName serviceComponent) {
+        if (serviceComponent == null) return null;
+        if (!mRemoteServices.containsKey(serviceComponent)) {
+            mRemoteServices.put(serviceComponent, new RemoteAppPredictionService(getContext(),
                     AppPredictionService.SERVICE_INTERFACE, serviceComponent, mUserId, this,
-                    mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+                    mMaster.isBindInstantServiceAllowed(), mMaster.verbose));
         }
 
-        return mRemoteService;
+        return mRemoteServices.get(serviceComponent);
     }
 
     private static final class AppPredictionSessionInfo {
         private static final boolean DEBUG = false;  // Do not submit with true
 
+        @NonNull
         private final AppPredictionSessionId mSessionId;
+        @NonNull
         private final AppPredictionContext mPredictionContext;
+        @Nullable
+        private final ComponentName mComponentName;
+        @NonNull
         private final Consumer<AppPredictionSessionId> mRemoveSessionInfoAction;
 
         private final RemoteCallbackList<IPredictionCallback> mCallbacks =
@@ -352,13 +391,17 @@
                     }
                 };
 
-        AppPredictionSessionInfo(AppPredictionSessionId id, AppPredictionContext predictionContext,
-                Consumer<AppPredictionSessionId> removeSessionInfoAction) {
+        AppPredictionSessionInfo(
+                @NonNull final AppPredictionSessionId id,
+                @NonNull final AppPredictionContext predictionContext,
+                @Nullable final ComponentName componentName,
+                @NonNull final Consumer<AppPredictionSessionId> removeSessionInfoAction) {
             if (DEBUG) {
                 Slog.d(TAG, "Creating AppPredictionSessionInfo for session Id=" + id);
             }
             mSessionId = id;
             mPredictionContext = predictionContext;
+            mComponentName = componentName;
             mRemoveSessionInfoAction = removeSessionInfoAction;
         }
 
@@ -390,8 +433,8 @@
         void resurrectSessionLocked(AppPredictionPerUserService service) {
             int callbackCount = mCallbacks.getRegisteredCallbackCount();
             if (DEBUG) {
-                Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
-                        + ") for session Id=" + mSessionId + " and "
+                Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked(
+                        mComponentName) + ") for session Id=" + mSessionId + " and "
                         + callbackCount + " callbacks.");
             }
             service.onCreatePredictionSessionLocked(mPredictionContext, mSessionId);
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
index 7c5a57c..ca89f7f 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.Binder;
 import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.IBinder;
@@ -662,6 +663,10 @@
                 referenceLost("TransportConnection.onServiceConnected()");
                 return;
             }
+            // TODO (b/147705255): Remove when binder calls to IBackupTransport are not blocking
+            // In short-term, blocking calls are OK as the transports come from the whitelist at
+            // {@link SystemConfig#getBackupTransportWhitelist()}
+            Binder.allowBlocking(binder);
             transportClient.onServiceConnected(binder);
         }
 
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 27b6bfb..bdcd832 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -67,6 +67,7 @@
     public static final int PACKAGE_TELEPHONY = 12;
     public static final int PACKAGE_WIFI = 13;
     public static final int PACKAGE_COMPANION = 14;
+    public static final int PACKAGE_RETAIL_DEMO = 15;
 
     @IntDef(value = {
             INTEGRITY_VERIFICATION_ALLOW,
@@ -105,6 +106,7 @@
         PACKAGE_TELEPHONY,
         PACKAGE_WIFI,
         PACKAGE_COMPANION,
+        PACKAGE_RETAIL_DEMO,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KnownPackage {}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index a1a953f..2968a81 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -6192,12 +6192,16 @@
         }
     }
 
-    private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, NetworkAgentInfo nai) {
-        int score = 0;
-        int serial = 0;
+    private void sendUpdatedScoreToFactories(@NonNull NetworkRequest networkRequest,
+            @Nullable NetworkAgentInfo nai) {
+        final int score;
+        final int serial;
         if (nai != null) {
             score = nai.getCurrentScore();
             serial = nai.factorySerialNumber;
+        } else {
+            score = 0;
+            serial = 0;
         }
         if (VDBG || DDBG){
             log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
@@ -6360,20 +6364,28 @@
         }
     }
 
-    private void makeDefault(@NonNull final NetworkAgentInfo newNetwork) {
+    private void makeDefault(@Nullable final NetworkAgentInfo newNetwork) {
         if (DBG) log("Switching to new default network: " + newNetwork);
 
+        mDefaultNetworkNai = newNetwork;
+
         try {
-            mNMS.setDefaultNetId(newNetwork.network.netId);
+            if (null != newNetwork) {
+                mNMS.setDefaultNetId(newNetwork.network.netId);
+            } else {
+                mNMS.clearDefaultNetId();
+            }
         } catch (Exception e) {
             loge("Exception setting default network :" + e);
         }
 
-        mDefaultNetworkNai = newNetwork;
         notifyLockdownVpn(newNetwork);
-        handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
-        updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
-        mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
+        handleApplyDefaultProxy(null != newNetwork
+                ? newNetwork.linkProperties.getHttpProxy() : null);
+        updateTcpBufferSizes(null != newNetwork
+                ? newNetwork.linkProperties.getTcpBufferSizes() : null);
+        mDnsManager.setDefaultDnsSystemProperties(null != newNetwork
+                ? newNetwork.linkProperties.getDnsServers() : Collections.EMPTY_LIST);
         notifyIfacesChangedForNetworkStats();
         // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
         updateAllVpnsCapabilities();
@@ -6452,6 +6464,16 @@
         void addRematchedNetwork(@NonNull final NetworkBgStatePair network) {
             mRematchedNetworks.add(network);
         }
+
+        // Will return null if this reassignment does not change the network assigned to
+        // the passed request.
+        @Nullable
+        private RequestReassignment getReassignment(@NonNull final NetworkRequestInfo nri) {
+            for (final RequestReassignment event : getRequestReassignments()) {
+                if (nri == event.mRequest) return event;
+            }
+            return null;
+        }
     }
 
     private ArrayMap<NetworkRequestInfo, NetworkAgentInfo> computeRequestReassignmentForNetwork(
@@ -6518,8 +6540,6 @@
             @NonNull final NetworkAgentInfo newNetwork, final long now) {
         ensureRunningOnConnectivityServiceThread();
         if (!newNetwork.everConnected) return;
-        boolean isNewDefault = false;
-        NetworkAgentInfo oldDefaultNetwork = null;
 
         changes.addRematchedNetwork(new NetworkReassignment.NetworkBgStatePair(newNetwork,
                 newNetwork.isBackgroundNetwork()));
@@ -6536,6 +6556,8 @@
             final NetworkRequestInfo nri = entry.getKey();
             final NetworkAgentInfo previousSatisfier = nri.mSatisfier;
             final NetworkAgentInfo newSatisfier = entry.getValue();
+            changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
+                    nri, previousSatisfier, newSatisfier));
             if (newSatisfier != null) {
                 if (VDBG) log("rematch for " + newSatisfier.name());
                 if (previousSatisfier != null) {
@@ -6548,25 +6570,9 @@
                     if (VDBG || DDBG) log("   accepting network in place of null");
                 }
                 newSatisfier.unlingerRequest(nri.request);
-                nri.mSatisfier = newSatisfier;
                 if (!newSatisfier.addRequest(nri.request)) {
                     Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
                 }
-                changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
-                        nri, previousSatisfier, newSatisfier));
-                // Tell NetworkProviders about the new score, so they can stop
-                // trying to connect if they know they cannot match it.
-                // TODO - this could get expensive if we have a lot of requests for this
-                // network.  Think about if there is a way to reduce this.  Push
-                // netid->request mapping to each provider?
-                sendUpdatedScoreToFactories(nri.request, newSatisfier);
-                if (isDefaultRequest(nri)) {
-                    isNewDefault = true;
-                    oldDefaultNetwork = previousSatisfier;
-                    if (previousSatisfier != null) {
-                        mLingerMonitor.noteLingerDefaultNetwork(previousSatisfier, newSatisfier);
-                    }
-                }
             } else {
                 // If "newNetwork" is listed as satisfying "nri" but no longer satisfies "nri",
                 // mark it as no longer satisfying "nri".  Because networks are processed by
@@ -6580,35 +6586,8 @@
                             " request " + nri.request.requestId);
                 }
                 newNetwork.removeRequest(nri.request.requestId);
-                if (previousSatisfier == newNetwork) {
-                    nri.mSatisfier = null;
-                    if (isDefaultRequest(nri)) mDefaultNetworkNai = null;
-                    sendUpdatedScoreToFactories(nri.request, null);
-                } else {
-                    Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
-                            newNetwork.name() +
-                            " without updating mSatisfier or providers!");
-                }
-                // TODO: Technically, sending CALLBACK_LOST here is
-                // incorrect if there is a replacement network currently
-                // connected that can satisfy nri, which is a request
-                // (not a listen). However, the only capability that can both
-                // a) be requested and b) change is NET_CAPABILITY_TRUSTED,
-                // so this code is only incorrect for a network that loses
-                // the TRUSTED capability, which is a rare case.
-                callCallbackForRequest(nri, newNetwork, ConnectivityManager.CALLBACK_LOST, 0);
             }
-        }
-
-        if (isNewDefault) {
-            updateDataActivityTracking(newNetwork, oldDefaultNetwork);
-            // Notify system services that this network is up.
-            makeDefault(newNetwork);
-            // Log 0 -> X and Y -> X default network transitions, where X is the new default.
-            mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
-                    now, newNetwork, oldDefaultNetwork);
-            // Have a new default network, release the transition wakelock in
-            scheduleReleaseNetworkTransitionWakelock();
+            nri.mSatisfier = newSatisfier;
         }
     }
 
@@ -6636,14 +6615,48 @@
             rematchNetworkAndRequests(changes, nai, now);
         }
 
-        final NetworkAgentInfo newDefaultNetwork = getDefaultNetwork();
+        final NetworkRequestInfo defaultRequestInfo = mNetworkRequests.get(mDefaultRequest);
+        final NetworkReassignment.RequestReassignment reassignment =
+                changes.getReassignment(defaultRequestInfo);
+        final NetworkAgentInfo newDefaultNetwork =
+                null != reassignment ? reassignment.mNewNetwork : oldDefaultNetwork;
+
+        if (oldDefaultNetwork != newDefaultNetwork) {
+            if (oldDefaultNetwork != null) {
+                mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
+            }
+            updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
+            // Notify system services of the new default.
+            makeDefault(newDefaultNetwork);
+            // Log 0 -> X and Y -> X default network transitions, where X is the new default.
+            mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
+                    now, newDefaultNetwork, oldDefaultNetwork);
+            // Have a new default network, release the transition wakelock in
+            scheduleReleaseNetworkTransitionWakelock();
+        }
 
         // Notify requested networks are available after the default net is switched, but
         // before LegacyTypeTracker sends legacy broadcasts
         for (final NetworkReassignment.RequestReassignment event :
                 changes.getRequestReassignments()) {
+            // Tell NetworkProviders about the new score, so they can stop
+            // trying to connect if they know they cannot match it.
+            // TODO - this could get expensive if there are a lot of outstanding requests for this
+            // network. Think of a way to reduce this. Push netid->request mapping to each factory?
+            sendUpdatedScoreToFactories(event.mRequest.request, event.mNewNetwork);
+
             if (null != event.mNewNetwork) {
                 notifyNetworkAvailable(event.mNewNetwork, event.mRequest);
+            } else {
+                // TODO: Technically, sending CALLBACK_LOST here is
+                // incorrect if there is a replacement network currently
+                // connected that can satisfy nri, which is a request
+                // (not a listen). However, the only capability that can both
+                // a) be requested and b) change is NET_CAPABILITY_TRUSTED,
+                // so this code is only incorrect for a network that loses
+                // the TRUSTED capability, which is a rare case.
+                callCallbackForRequest(event.mRequest, event.mOldNetwork,
+                        ConnectivityManager.CALLBACK_LOST, 0);
             }
         }
 
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index b1584fe..e3c7325 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -23,7 +23,6 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.SystemApi.Client;
-import android.annotation.SystemApi.Process;
 import android.annotation.UserIdInt;
 import android.app.ActivityThread;
 import android.content.Context;
@@ -65,7 +64,7 @@
  *
  * {@hide}
  */
-@SystemApi(client = Client.MODULE_LIBRARIES, process = Process.SYSTEM_SERVER)
+@SystemApi(client = Client.SYSTEM_SERVER)
 public abstract class SystemService {
 
     /** @hide */
@@ -132,7 +131,7 @@
      * Class representing user in question in the lifecycle callbacks.
      * @hide
      */
-    @SystemApi(client = Client.MODULE_LIBRARIES, process = Process.SYSTEM_SERVER)
+    @SystemApi(client = Client.SYSTEM_SERVER)
     public static final class TargetUser {
         @NonNull
         private final UserInfo mUserInfo;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 56f9424..cbf6c27 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -285,7 +285,8 @@
                 PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
                         | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
                         | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
-                        | PhoneStateListener.LISTEN_REGISTRATION_FAILURE;
+                        | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
+                        | PhoneStateListener.LISTEN_BARRING_INFO;
 
     static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_PRECISE_CALL_STATE
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 79b3a7b..f7d7d6c 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -65,13 +65,13 @@
 import android.util.DebugUtils;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.StatsLog;
 import android.util.proto.ProtoOutputStream;
 import android.view.InputDevice;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -1370,8 +1370,8 @@
     private void noteVibratorOnLocked(int uid, long millis) {
         try {
             mBatteryStatsService.noteVibratorOn(uid, millis);
-            StatsLog.write_non_chained(StatsLog.VIBRATOR_STATE_CHANGED, uid, null,
-                    StatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, millis);
+            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED, uid, null,
+                    FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, millis);
             mCurVibUid = uid;
             mIsVibrating = true;
         } catch (RemoteException e) {
@@ -1382,8 +1382,8 @@
         if (mCurVibUid >= 0) {
             try {
                 mBatteryStatsService.noteVibratorOff(mCurVibUid);
-                StatsLog.write_non_chained(StatsLog.VIBRATOR_STATE_CHANGED, mCurVibUid, null,
-                        StatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF, 0);
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
+                        mCurVibUid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF, 0);
             } catch (RemoteException e) { }
             mCurVibUid = -1;
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6560777..148f7de 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5143,14 +5143,12 @@
             checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
             app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
         } catch (Exception e) {
-            // todo: Yikes!  What should we do?  For now we will try to
-            // start another process, but that could easily get us in
-            // an infinite loop of restarting processes...
+            // We need kill the process group here. (b/148588589)
             Slog.wtf(TAG, "Exception thrown during bind of " + app, e);
-
             app.resetPackageList(mProcessStats);
             app.unlinkDeathRecipient();
-            mProcessList.startProcessLocked(app, new HostingRecord("bind-fail", processName));
+            app.kill("error during bind", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE, true);
+            handleAppDiedLocked(app, false, true);
             return false;
         }
 
@@ -18924,19 +18922,16 @@
         }
 
         // The arguments here are untyped because the base ActivityManagerInternal class
-        // doesn't have compile-time visiblity into ActivityServiceConnectionHolder or
+        // doesn't have compile-time visibility into ActivityServiceConnectionHolder or
         // ConnectionRecord.
         @Override
-        public void disconnectActivityFromServices(Object connectionHolder, Object conns) {
+        public void disconnectActivityFromServices(Object connectionHolder) {
             // 'connectionHolder' is an untyped ActivityServiceConnectionsHolder
-            // 'conns' is an untyped HashSet<ConnectionRecord>
             final ActivityServiceConnectionsHolder holder =
                     (ActivityServiceConnectionsHolder) connectionHolder;
-            final HashSet<ConnectionRecord> toDisconnect = (HashSet<ConnectionRecord>) conns;
-            synchronized(ActivityManagerService.this) {
-                for (ConnectionRecord cr : toDisconnect) {
-                    mServices.removeConnectionLocked(cr, null, holder);
-                }
+            synchronized (ActivityManagerService.this) {
+                holder.forEachConnection(cr -> mServices.removeConnectionLocked(
+                        (ConnectionRecord) cr, null /* skipApp */, holder /* skipAct */));
             }
         }
 
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3c7d6b8..71486d3 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1572,8 +1572,11 @@
         if (userGid != UserHandle.ERR_GID) {
             gidList.add(userGid);
         }
-        if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
+        if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE
+                || mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
             // For DownloadProviders and MTP: To grant access to /sdcard/Android/
+            // And a special case for the FUSE daemon since it runs an MTP server and should have
+            // access to Android/
             gidList.add(UserHandle.getUid(UserHandle.getUserId(uid), Process.SDCARD_RW_GID));
         }
         if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index fc33c25..2b4d15e 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -615,13 +615,20 @@
             int _uid) {
         mService = _service;
         info = _info;
+        ProcessInfo procInfo = null;
         if (_service.mPackageManagerInt != null) {
             ArrayMap<String, ProcessInfo> processes =
                     _service.mPackageManagerInt.getProcessesForUid(_uid);
-            processInfo = processes != null ? processes.get(_processName) : null;
-        } else {
-            processInfo = null;
+            if (processes != null) {
+                procInfo = processes.get(_processName);
+                if (procInfo != null && procInfo.deniedPermissions == null) {
+                    // If this process hasn't asked for permissions to be denied, then
+                    // we don't care about it.
+                    procInfo = null;
+                }
+            }
         }
+        processInfo = procInfo;
         isolated = _info.uid != _uid;
         appZygote = (UserHandle.getAppId(_uid) >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
                 && UserHandle.getAppId(_uid) <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index bc4707f..4061df4 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -54,6 +54,14 @@
           "include-filter": "com.android.server.am."
         }
       ]
+    },
+    {
+      "name": "CtsAppSecurityHostTestCases",
+      "options": [
+        {
+          "include-filter": "android.appsecurity.cts.AppDataIsolationTests"
+        }
+      ]
     }
   ]
 }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 531bc5d..ee2d7fc 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -846,7 +846,12 @@
          */
         public void started(@NonNull IBinder clientId, @AppOpsManager.UidState int uidState)
                 throws RemoteException {
-            if (!parent.isRunning()) {
+            started(clientId, uidState, true);
+        }
+
+        private void started(@NonNull IBinder clientId, @AppOpsManager.UidState int uidState,
+                boolean triggerCallbackIfNeeded) throws RemoteException {
+            if (triggerCallbackIfNeeded && !parent.isRunning()) {
                 scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
                         parent.packageName, true);
             }
@@ -965,8 +970,16 @@
 
                 if (event.getUidState() != newState) {
                     try {
+                        // Remove all but one unfinished start count and then call finished() to
+                        // remove start event object
+                        int numPreviousUnfinishedStarts = event.numUnfinishedStarts;
+                        event.numUnfinishedStarts = 1;
                         finished(event.getClientId(), false);
-                        started(event.getClientId(), newState);
+
+                        // Call started() to add a new start event object and then add the
+                        // previously removed unfinished start counts back
+                        started(event.getClientId(), newState, false);
+                        event.numUnfinishedStarts += numPreviousUnfinishedStarts - 1;
                     } catch (RemoteException e) {
                         if (DEBUG) Slog.e(TAG, "Cannot switch to new uidState " + newState);
                     }
@@ -1948,6 +1961,11 @@
 
     @Override
     public void setUidMode(int code, int uid, int mode) {
+        setUidMode(code, uid, mode, null);
+    }
+
+    private void setUidMode(int code, int uid, int mode,
+            @Nullable IAppOpsCallback callbackToIgnore) {
         if (DEBUG) {
             Slog.i(TAG, "uid " + uid + " OP_" + opToName(code) + " := " + modeToName(mode)
                     + " by uid " + Binder.getCallingUid());
@@ -2031,6 +2049,10 @@
                     }
                 }
             }
+
+            if (callbackSpecs != null && callbackToIgnore != null) {
+                callbackSpecs.remove(mModeWatchers.get(callbackToIgnore.asBinder()));
+            }
         }
 
         if (callbackSpecs == null) {
@@ -2078,6 +2100,11 @@
                 continue;
             }
 
+            if (packageManager.checkPermission(permissionName, packageName)
+                    != PackageManager.PERMISSION_GRANTED) {
+                continue;
+            }
+
             PermissionInfo permissionInfo;
             try {
                 permissionInfo = packageManager.getPermissionInfo(permissionName, 0);
@@ -2158,6 +2185,11 @@
      */
     @Override
     public void setMode(int code, int uid, @NonNull String packageName, int mode) {
+        setMode(code, uid, packageName, mode, null);
+    }
+
+    private void setMode(int code, int uid, @NonNull String packageName, int mode,
+            @Nullable IAppOpsCallback callbackToIgnore) {
         enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
         verifyIncomingOp(code);
         ArraySet<ModeCallback> repCbs = null;
@@ -2201,6 +2233,9 @@
                         }
                         repCbs.addAll(cbs);
                     }
+                    if (repCbs != null && callbackToIgnore != null) {
+                        repCbs.remove(mModeWatchers.get(callbackToIgnore.asBinder()));
+                    }
                     if (mode == AppOpsManager.opToDefaultMode(op.op)) {
                         // If going into the default mode, prune this op
                         // if there is nothing else interesting in it.
@@ -5587,5 +5622,17 @@
                 boolean visible) {
             AppOpsService.this.updateAppWidgetVisibility(uidPackageNames, visible);
         }
+
+        @Override
+        public void setUidModeIgnoringCallback(int code, int uid, int mode,
+                @Nullable IAppOpsCallback callbackToIgnore) {
+            setUidMode(code, uid, mode, callbackToIgnore);
+        }
+
+        @Override
+        public void setModeIgnoringCallback(int code, int uid, @NonNull String packageName,
+                int mode, @Nullable IAppOpsCallback callbackToIgnore) {
+            setMode(code, uid, packageName, mode, callbackToIgnore);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
index 929dfc4..7071510 100644
--- a/services/core/java/com/android/server/connectivity/LingerMonitor.java
+++ b/services/core/java/com/android/server/connectivity/LingerMonitor.java
@@ -16,6 +16,10 @@
 
 package com.android.server.connectivity;
 
+import static android.net.ConnectivityManager.NETID_UNSET;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -27,18 +31,16 @@
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.SparseIntArray;
 import android.util.SparseBooleanArray;
-import java.util.Arrays;
-import java.util.HashMap;
+import android.util.SparseIntArray;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.MessageUtils;
-import com.android.server.connectivity.NetworkNotificationManager;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
 
-import static android.net.ConnectivityManager.NETID_UNSET;
+import java.util.Arrays;
+import java.util.HashMap;
 
 /**
  * Class that monitors default network linger events and possibly notifies the user of network
@@ -206,8 +208,19 @@
         mEverNotified.put(fromNai.network.netId, true);
     }
 
+    /**
+     * Put up or dismiss a notification or toast for of a change in the default network if needed.
+     *
+     * Putting up a notification when switching from no network to some network is not supported
+     * and as such this method can't be called with a null |fromNai|. It can be called with a
+     * null |toNai| if there isn't a default network any more.
+     *
+     * @param fromNai switching from this NAI
+     * @param toNai switching to this NAI
+     */
     // The default network changed from fromNai to toNai due to a change in score.
-    public void noteLingerDefaultNetwork(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
+    public void noteLingerDefaultNetwork(@NonNull final NetworkAgentInfo fromNai,
+            @Nullable final NetworkAgentInfo toNai) {
         if (VDBG) {
             Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.name() +
                     " everValidated=" + fromNai.everValidated +
@@ -221,6 +234,10 @@
         // Internet access).
         maybeStopNotifying(fromNai);
 
+        // If the network was simply lost (either because it disconnected or because it stopped
+        // being the default with no replacement), then don't show a notification.
+        if (null == toNai) return;
+
         // If this network never validated, don't notify. Otherwise, we could do things like:
         //
         // 1. Unvalidated wifi connects.
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerService.java b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
index 5876d43..64f25dd 100644
--- a/services/core/java/com/android/server/incremental/IncrementalManagerService.java
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
@@ -32,7 +32,10 @@
 import android.os.incremental.IIncrementalManager;
 import android.util.Slog;
 
+import com.android.internal.util.DumpUtils;
+
 import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
 /**
  * This service has the following purposes:
@@ -71,6 +74,13 @@
         mNativeInstance = nativeStartService();
     }
 
+    @SuppressWarnings("resource")
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
+        nativeDump(mNativeInstance, fd.getInt$());
+    }
+
     /**
      * Notifies native IIncrementalManager service that system is ready.
      */
@@ -158,4 +168,6 @@
     private static native long nativeStartService();
 
     private static native void nativeSystemReady(long nativeInstance);
+
+    private static native void nativeDump(long nativeInstance, int fd);
 }
diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
new file mode 100644
index 0000000..16b9eb9
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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.server.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.session.MediaSession;
+import android.view.KeyEvent;
+
+/**
+ * Provides a way to customize behavior for media key events.
+ */
+public interface MediaKeyDispatcher {
+    /**
+     * Implement this to customize the logic for which MediaSession should consume which key event.
+     *
+     * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons.
+     * @param asSystemService {@code true} if the event came from the system service via hardware
+     *         devices. {@code false} if the event came from the app process through key injection.
+     * @return a {@link MediaSession.Token} instance that should consume the given key event.
+     */
+    @Nullable
+    MediaSession.Token getSessionForKeyEvent(@NonNull KeyEvent keyEvent,
+            boolean asSystemService);
+}
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index b21d2e7..820731d 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -50,15 +50,18 @@
     private final MediaSessionService mService;
     @GuardedBy("mLock")
     private boolean mIsConnected;
+    @GuardedBy("mLock")
+    private int mPolicies;
 
     public MediaSession2Record(Session2Token sessionToken, MediaSessionService service,
-            Looper handlerLooper) {
+            Looper handlerLooper, int policies) {
         mSessionToken = sessionToken;
         mService = service;
         mHandlerExecutor = new HandlerExecutor(new Handler(handlerLooper));
         mController = new MediaController2.Builder(service.getContext(), sessionToken)
                 .setControllerCallback(mHandlerExecutor, new Controller2Callback())
                 .build();
+        mPolicies = policies;
     }
 
     @Override
@@ -129,6 +132,21 @@
         return false;
     }
 
+
+    @Override
+    public int getSessionPolicies() {
+        synchronized (mLock) {
+            return mPolicies;
+        }
+    }
+
+    @Override
+    public void setSessionPolicies(int policies) {
+        synchronized (mLock) {
+            mPolicies = policies;
+        }
+    }
+
     @Override
     public void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + "token=" + mSessionToken);
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 46fb240..05f7e1d 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -159,9 +159,12 @@
     private long mDuration = -1;
     private String mMetadataDescription;
 
+    private int mPolicies;
+
     public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
             ISessionCallback cb, String tag, Bundle sessionInfo,
-            MediaSessionService service, Looper handlerLooper) throws RemoteException {
+            MediaSessionService service, Looper handlerLooper, int policies)
+            throws RemoteException {
         mOwnerPid = ownerPid;
         mOwnerUid = ownerUid;
         mUserId = userId;
@@ -178,6 +181,7 @@
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
         mAudioAttrs = DEFAULT_ATTRIBUTES;
+        mPolicies = policies;
 
         // May throw RemoteException if the session app is killed.
         mSessionCb.mCb.asBinder().linkToDeath(this, 0);
@@ -438,6 +442,20 @@
     }
 
     @Override
+    public int getSessionPolicies() {
+        synchronized (mLock) {
+            return mPolicies;
+        }
+    }
+
+    @Override
+    public void setSessionPolicies(int policies) {
+        synchronized (mLock) {
+            mPolicies = policies;
+        }
+    }
+
+    @Override
     public void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + mTag + " " + this);
 
@@ -808,6 +826,9 @@
 
         @Override
         public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
+            if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) == 1) {
+                return;
+            }
             mMediaButtonReceiver = pi;
             final long token = Binder.clearCallingIdentity();
             try {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 2cde89a7..6e10880 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -20,6 +20,8 @@
 import android.os.ResultReceiver;
 import android.view.KeyEvent;
 
+import com.android.server.media.SessionPolicyProvider.SessionPolicy;
+
 import java.io.PrintWriter;
 
 /**
@@ -128,6 +130,18 @@
             KeyEvent ke, int sequenceId, ResultReceiver cb);
 
     /**
+     * Get session policies from custom policy provider set when MediaSessionRecord is instantiated.
+     * If custom policy does not exist, will return null.
+     */
+    @SessionPolicy
+    int getSessionPolicies();
+
+    /**
+     * Overwrite session policies that have been set when MediaSessionRecord is instantiated.
+     */
+    void setSessionPolicies(@SessionPolicy int policies);
+
+    /**
      * Dumps internal state
      *
      * @param pw print writer
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index a6ad57a..d0efef0 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -141,6 +141,9 @@
     final RemoteCallbackList<IRemoteVolumeController> mRemoteVolumeControllers =
             new RemoteCallbackList<>();
 
+    private SessionPolicyProvider mCustomSessionPolicyProvider;
+    private MediaKeyDispatcher mCustomMediaKeyDispatcher;
+
     public MediaSessionService(Context context) {
         super(context);
         mContext = context;
@@ -179,6 +182,9 @@
         mSettingsObserver.observe();
         mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_LEANBACK);
+
+        // TODO: (jinpark) check if config value for custom MediaKeyDispatcher and
+        //  SessionPolicyProvider have been overlayed and instantiate using reflection.
         updateUser();
     }
 
@@ -555,7 +561,8 @@
      * 4. It needs to be added to the relevant user record.
      */
     private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
-            String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) {
+            String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo,
+            int policies) {
         synchronized (mLock) {
             FullUserRecord user = getFullUserRecordLocked(userId);
             if (user == null) {
@@ -566,7 +573,8 @@
             final MediaSessionRecord session;
             try {
                 session = new MediaSessionRecord(callerPid, callerUid, userId,
-                        callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper());
+                        callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper(),
+                        policies);
             } catch (RemoteException e) {
                 throw new RuntimeException("Media Session owner died prematurely.", e);
             }
@@ -1127,8 +1135,11 @@
                 if (cb == null) {
                     throw new IllegalArgumentException("Controller callback cannot be null");
                 }
+                int policies = (mCustomSessionPolicyProvider != null)
+                        ? mCustomSessionPolicyProvider.getSessionPoliciesForApplication(
+                                uid, packageName) : 0;
                 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag,
-                        sessionInfo).getSessionBinder();
+                        sessionInfo, policies).getSessionBinder();
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1148,7 +1159,7 @@
                             + " but actually=" + sessionToken.getUid());
                 }
                 MediaSession2Record record = new MediaSession2Record(
-                        sessionToken, MediaSessionService.this, mHandler.getLooper());
+                        sessionToken, MediaSessionService.this, mHandler.getLooper(), 0);
                 synchronized (mLock) {
                     FullUserRecord user = getFullUserRecordLocked(record.getUserId());
                     user.mPriorityStack.addSession(record);
@@ -1308,12 +1319,11 @@
          * ACTION_MEDIA_BUTTON intent to the rest of the system.
          *
          * @param packageName The caller package
-         * @param asSystemService {@code true} if the event sent to the session as if it was come
-         *          from the system service instead of the app process. This helps sessions to
-         *          distinguish between the key injection by the app and key events from the
-         *          hardware devices. Should be used only when the volume key events aren't handled
-         *          by foreground activity. {@code false} otherwise to tell session about the real
-         *          caller.
+         * @param asSystemService {@code true} if the event sent to the session came from the
+         *          service instead of the app process. This helps sessions to distinguish between
+         *          the key injection by the app and key events from the hardware devices. Should be
+         *          used only when the hardware key events aren't handled by foreground activity.
+         *          {@code false} otherwise to tell session about the real caller.
          * @param keyEvent a non-null KeyEvent whose key code is one of the
          *            supported media buttons
          * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
@@ -2115,14 +2125,27 @@
                 // TODO(jaewan): Implement
                 return;
             }
-            MediaSessionRecord session =
-                    (MediaSessionRecord) mCurrentFullUserRecord.getMediaButtonSessionLocked();
+            MediaSessionRecord session = null;
+
+            // Retrieve custom session for key event if it exists.
+            if (mCustomMediaKeyDispatcher != null) {
+                MediaSession.Token token =
+                        mCustomMediaKeyDispatcher.getSessionForKeyEvent(keyEvent, asSystemService);
+                if (token != null) {
+                    session = getMediaSessionRecordLocked(token);
+                }
+            }
+
+            if (session == null) {
+                session = (MediaSessionRecord) mCurrentFullUserRecord.getMediaButtonSessionLocked();
+            }
+
             if (session != null) {
                 if (DEBUG_KEY_EVENT) {
                     Log.d(TAG, "Sending " + keyEvent + " to " + session);
                 }
                 if (needWakeLock) {
-                    mKeyEventReceiver.aquireWakeLockLocked();
+                    mKeyEventReceiver.acquireWakeLockLocked();
                 }
                 // If we don't need a wakelock use -1 as the id so we won't release it later.
                 session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent,
@@ -2140,7 +2163,7 @@
             } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
                     || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
                 if (needWakeLock) {
-                    mKeyEventReceiver.aquireWakeLockLocked();
+                    mKeyEventReceiver.acquireWakeLockLocked();
                 }
                 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
                 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -2349,7 +2372,7 @@
                 }
             }
 
-            public void aquireWakeLockLocked() {
+            public void acquireWakeLockLocked() {
                 if (mRefCount == 0) {
                     mMediaEventWakeLock.acquire();
                 }
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 7bb7cf4..07b1a1a 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -274,6 +274,15 @@
     }
 
     private void updateMediaButtonSession(MediaSessionRecordImpl newMediaButtonSession) {
+        // Check if the policy states that this session should not be updated as a media button
+        // session.
+        if (newMediaButtonSession != null) {
+            int policies = newMediaButtonSession.getSessionPolicies();
+            if ((policies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_SESSION) == 1) {
+                return;
+            }
+        }
+
         MediaSessionRecordImpl oldMediaButtonSession = mMediaButtonSession;
         mMediaButtonSession = newMediaButtonSession;
         mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged(
diff --git a/services/core/java/com/android/server/media/SessionPolicyProvider.java b/services/core/java/com/android/server/media/SessionPolicyProvider.java
new file mode 100644
index 0000000..6eb79ef
--- /dev/null
+++ b/services/core/java/com/android/server/media/SessionPolicyProvider.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 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.server.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.media.session.MediaSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Interface for customizing {@link MediaSessionService}
+ */
+public interface SessionPolicyProvider {
+    @IntDef(value = {
+            SESSION_POLICY_IGNORE_BUTTON_RECEIVER,
+            SESSION_POLICY_IGNORE_BUTTON_SESSION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface SessionPolicy {}
+
+    /**
+     * Policy to ignore media button receiver, to not revive the media app when its media session is
+     * released or the app is dead.
+     *
+     * @see MediaSession#setMediaButtonReceiver
+     */
+    int SESSION_POLICY_IGNORE_BUTTON_RECEIVER = 1 << 0;
+
+    /**
+     * Policy to ignore sessions that should not respond to media key events via
+     * {@link MediaSessionService}. A typical use case is to explicitly
+     * ignore sessions that should not respond to media key events even if their playback state has
+     * changed most recently.
+     */
+    int SESSION_POLICY_IGNORE_BUTTON_SESSION = 1 << 1;
+
+    /**
+     * Use this to statically set policies for sessions when they are created.
+     * Use android.media.session.MediaSessionManager#setSessionPolicies(MediaSession.Token, int)
+     * to dynamically change policies at runtime.
+     *
+     * @param uid
+     * @param packageName
+     * @return list of policies
+     */
+    @SessionPolicy int getSessionPoliciesForApplication(int uid, @NonNull String packageName);
+}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 00b4c2b..d613799 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -16,7 +16,7 @@
 
 package com.android.server.notification;
 
-import android.util.StatsLog;
+import com.android.internal.util.FrameworkStatsLog;
 
 /**
  * Standard implementation of NotificationRecordLogger interface.
@@ -31,7 +31,7 @@
         if (!p.shouldLog(buzzBeepBlink)) {
             return;
         }
-        StatsLog.write(StatsLog.NOTIFICATION_REPORTED,
+        FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED,
                 /* int32 event_id = 1 */ p.getUiEvent().getId(),
                 /* int32 uid = 2 */ r.getUid(),
                 /* string package_name = 3 */ r.sbn.getPackageName(),
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 38da8ab..bf7bebd 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -101,7 +101,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.incremental.IncrementalFileStorages;
-import android.os.incremental.IncrementalManager;
 import android.os.storage.StorageManager;
 import android.provider.Settings.Secure;
 import android.stats.devicepolicy.DevicePolicyEnums;
@@ -559,17 +558,6 @@
         mStagedSessionErrorMessage =
                 stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
 
-        // TODO(b/136132412): sanity check if session should not be incremental
-        if (!params.isStaged && isIncrementalInstallation()) {
-            IncrementalManager incrementalManager = (IncrementalManager) mContext.getSystemService(
-                    Context.INCREMENTAL_SERVICE);
-            if (incrementalManager != null) {
-                mIncrementalFileStorages =
-                        new IncrementalFileStorages(mPackageName, stageDir, incrementalManager,
-                                params.dataLoaderParams);
-            }
-        }
-
         if (isStreamingInstallation()
                 && this.params.dataLoaderParams.getComponentName().getPackageName()
                 == SYSTEM_DATA_LOADER_PACKAGE) {
@@ -1040,10 +1028,6 @@
             }
         }
 
-        if (mIncrementalFileStorages != null) {
-            mIncrementalFileStorages.finishSetUp();
-        }
-
         dispatchStreamValidateAndCommit();
     }
 
@@ -1052,11 +1036,6 @@
     }
 
     private void handleStreamValidateAndCommit() {
-        // TODO(b/136132412): update with new APIs
-        if (mIncrementalFileStorages != null) {
-            mIncrementalFileStorages.startLoading();
-        }
-
         boolean success = streamValidateAndCommit();
 
         if (isMultiPackage()) {
@@ -2476,17 +2455,15 @@
             }
         }
 
-        if (mIncrementalFileStorages != null) {
-            for (InstallationFile file : addedFiles) {
-                try {
-                    mIncrementalFileStorages.addFile(file);
-                } catch (IOException ex) {
-                    // TODO(b/146080380): add incremental-specific error code
-                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                            "Failed to add and configure Incremental File: " + file.getName(), ex);
-                }
+        // TODO(b/136132412): update with new APIs
+        if (isIncrementalInstallation()) {
+            try {
+                mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext,
+                        stageDir, params.dataLoaderParams, addedFiles);
+                return true;
+            } catch (IOException e) {
+                throw new PackageManagerException(e);
             }
-            return true;
         }
 
         final DataLoaderManager dataLoaderManager = mContext.getSystemService(
@@ -2761,13 +2738,14 @@
                 bridge.forceClose();
             }
         }
+        if (mIncrementalFileStorages != null) {
+            mIncrementalFileStorages.cleanUp();
+            mIncrementalFileStorages = null;
+        }
         // For staged sessions, we don't delete the directory where the packages have been copied,
         // since these packages are supposed to be read on reboot.
         // Those dirs are deleted when the staged session has reached a final state.
         if (stageDir != null && !params.isStaged) {
-            if (mIncrementalFileStorages != null) {
-                mIncrementalFileStorages.cleanUp();
-            }
             try {
                 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
             } catch (InstallerException ignored) {
@@ -2783,6 +2761,7 @@
         } else {
             if (mIncrementalFileStorages != null) {
                 mIncrementalFileStorages.cleanUp();
+                mIncrementalFileStorages = null;
             }
             try {
                 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c09fb38..93e724e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -55,6 +55,7 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
 import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
@@ -206,6 +207,7 @@
 import android.content.pm.dex.IArtManager;
 import android.content.pm.parsing.AndroidPackage;
 import android.content.pm.parsing.ApkParseUtils;
+import android.content.pm.parsing.ComponentParseUtils;
 import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
 import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
 import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
@@ -1544,6 +1546,7 @@
     final @Nullable String[] mTelephonyPackages;
     final @NonNull String mServicesExtensionPackageName;
     final @NonNull String mSharedSystemSharedLibraryPackageName;
+    final @Nullable String mRetailDemoPackage;
 
     private final PackageUsage mPackageUsage = new PackageUsage();
     private final CompilerStats mCompilerStats = new CompilerStats();
@@ -3160,6 +3163,7 @@
             mAppPredictionServicePackage = getAppPredictionServicePackageName();
             mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
             mTelephonyPackages = getTelephonyPackageNames();
+            mRetailDemoPackage = getRetailDemoPackageName();
 
             // Now that we know all of the shared libraries, update all clients to have
             // the correct library paths.
@@ -11252,6 +11256,26 @@
         return object;
     }
 
+    private <T extends ComponentParseUtils.ParsedMainComponent>
+            void assertPackageProcesses(AndroidPackage pkg, List<T> components,
+            ArrayMap<String, ComponentParseUtils.ParsedProcess> procs, String compName)
+            throws PackageManagerException {
+        if (components == null) {
+            return;
+        }
+        for (int i = components.size() - 1; i >= 0; i--) {
+            final ComponentParseUtils.ParsedMainComponent<?> component = components.get(i);
+            if (!procs.containsKey(component.getProcessName())) {
+                throw new PackageManagerException(
+                        INSTALL_FAILED_PROCESS_NOT_DEFINED,
+                        "Can't install because " + compName + " " + component.className
+                                + "'s process attribute " + component.getProcessName()
+                                + " (in package " + pkg.getPackageName()
+                                + ") is not included in the <processes> list");
+            }
+        }
+    }
+
     /**
      * Asserts the parsed package is valid according to the given policy. If the
      * package is invalid, for whatever reason, throws {@link PackageManagerException}.
@@ -11481,6 +11505,24 @@
                 mComponentResolver.assertProvidersNotDefined(pkg);
             }
 
+            // If this package has defined explicit processes, then ensure that these are
+            // the only processes used by its components.
+            final ArrayMap<String, ComponentParseUtils.ParsedProcess> procs = pkg.getProcesses();
+            if (procs != null) {
+                if (!procs.containsKey(pkg.getProcessName())) {
+                    throw new PackageManagerException(
+                            INSTALL_FAILED_PROCESS_NOT_DEFINED,
+                            "Can't install because application tag's process attribute "
+                                    + pkg.getProcessName()
+                                    + " (in package " + pkg.getPackageName()
+                                    + ") is not included in the <processes> list");
+                }
+                assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
+                assertPackageProcesses(pkg, pkg.getServices(), procs, "service");
+                assertPackageProcesses(pkg, pkg.getReceivers(), procs, "receiver");
+                assertPackageProcesses(pkg, pkg.getProviders(), procs, "provider");
+            }
+
             // Verify that packages sharing a user with a privileged app are marked as privileged.
             if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
                 SharedUserSetting sharedUserSetting = null;
@@ -19723,6 +19765,41 @@
     }
 
     @Nullable
+    private String getRetailDemoPackageName() {
+        final String predefinedPkgName = mContext.getString(R.string.config_retailDemoPackage);
+        final String predefinedSignature = mContext.getString(
+                R.string.config_retailDemoPackageSignature);
+
+        if (TextUtils.isEmpty(predefinedPkgName) || TextUtils.isEmpty(predefinedSignature)) {
+            return null;
+        }
+
+        final AndroidPackage androidPkg = mPackages.get(predefinedPkgName);
+        if (androidPkg != null) {
+            final SigningDetails signingDetail = androidPkg.getSigningDetails();
+            if (signingDetail != null && signingDetail.signatures != null) {
+                try {
+                    final MessageDigest msgDigest = MessageDigest.getInstance("SHA-256");
+                    for (Signature signature : signingDetail.signatures) {
+                        if (TextUtils.equals(predefinedSignature,
+                                HexEncoding.encodeToString(msgDigest.digest(
+                                        signature.toByteArray()), false))) {
+                            return predefinedPkgName;
+                        }
+                    }
+                } catch (NoSuchAlgorithmException e) {
+                    Slog.e(
+                            TAG,
+                            "Unable to verify signatures as getting the retail demo package name",
+                            e);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
     private String ensureSystemPackageName(@Nullable String packageName) {
         if (packageName == null) {
             return null;
@@ -23010,6 +23087,10 @@
                     return filterOnlySystemPackages(mTelephonyPackages);
                 case PackageManagerInternal.PACKAGE_COMPANION:
                     return filterOnlySystemPackages("com.android.companiondevicemanager");
+                case PackageManagerInternal.PACKAGE_RETAIL_DEMO:
+                    return TextUtils.isEmpty(mRetailDemoPackage)
+                            ? ArrayUtils.emptyArray(String.class)
+                            : new String[] {mRetailDemoPackage};
                 default:
                     return ArrayUtils.emptyArray(String.class);
             }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index fcd8e22..fbea595 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4570,7 +4570,7 @@
                 pw.print("anyDensity");
             }
             pw.println("]");
-            List<String> libraryNames = pkg.getLibraryNames();
+            final List<String> libraryNames = pkg.getLibraryNames();
             if (libraryNames != null && libraryNames.size() > 0) {
                 pw.print(prefix); pw.println("  dynamic libraries:");
                 for (int i = 0; i< libraryNames.size(); i++) {
@@ -4585,7 +4585,7 @@
                 pw.print(" version:"); pw.println(pkg.getStaticSharedLibVersion());
             }
 
-            List<String> usesLibraries = pkg.getUsesLibraries();
+            final List<String> usesLibraries = pkg.getUsesLibraries();
             if (usesLibraries != null && usesLibraries.size() > 0) {
                 pw.print(prefix); pw.println("  usesLibraries:");
                 for (int i=0; i< usesLibraries.size(); i++) {
@@ -4593,8 +4593,8 @@
                 }
             }
 
-            List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
-            long[] usesStaticLibrariesVersions = pkg.getUsesStaticLibrariesVersions();
+            final List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
+            final long[] usesStaticLibrariesVersions = pkg.getUsesStaticLibrariesVersions();
             if (usesStaticLibraries != null
                     && usesStaticLibraries.size() > 0) {
                 pw.print(prefix); pw.println("  usesStaticLibraries:");
@@ -4605,7 +4605,7 @@
                 }
             }
 
-            List<String> usesOptionalLibraries = pkg.getUsesOptionalLibraries();
+            final List<String> usesOptionalLibraries = pkg.getUsesOptionalLibraries();
             if (usesOptionalLibraries != null
                     && usesOptionalLibraries.size() > 0) {
                 pw.print(prefix); pw.println("  usesOptionalLibraries:");
@@ -4615,7 +4615,7 @@
                 }
             }
 
-            String[] usesLibraryFiles = pkg.getUsesLibraryFiles();
+            final String[] usesLibraryFiles = pkg.getUsesLibraryFiles();
             if (usesLibraryFiles != null
                     && usesLibraryFiles.length > 0) {
                 pw.print(prefix); pw.println("  usesLibraryFiles:");
@@ -4623,6 +4623,20 @@
                     pw.print(prefix); pw.print("    "); pw.println(usesLibraryFiles[i]);
                 }
             }
+            final ArrayMap<String, ComponentParseUtils.ParsedProcess> procs = pkg.getProcesses();
+            if (procs != null) {
+                pw.print(prefix); pw.println("  processes:");
+                for (int i = 0; i < procs.size(); i++) {
+                    final ComponentParseUtils.ParsedProcess proc = procs.valueAt(i);
+                    pw.print(prefix); pw.print("    "); pw.println(proc.name);
+                    if (proc.deniedPermissions != null) {
+                        for (int j = 0; j < proc.deniedPermissions.size(); j++) {
+                            pw.print(prefix); pw.print("      deny: ");
+                            pw.println(proc.deniedPermissions.valueAt(j));
+                        }
+                    }
+                }
+            }
         }
         pw.print(prefix); pw.print("  timeStamp=");
             date.setTime(ps.timeStamp);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index f368666..f7889ea 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1977,10 +1977,15 @@
      * After validating the caller, it passes the request to {@link #mShortcutRequestPinProcessor}.
      * Either {@param shortcut} or {@param appWidget} should be non-null.
      */
-    private boolean requestPinItem(String packageName, int userId, ShortcutInfo shortcut,
+    private boolean requestPinItem(String callingPackage, int userId, ShortcutInfo shortcut,
             AppWidgetProviderInfo appWidget, Bundle extras, IntentSender resultIntent) {
-        verifyCaller(packageName, userId);
-        verifyShortcutInfoPackage(packageName, shortcut);
+        verifyCaller(callingPackage, userId);
+        if (shortcut == null || !injectHasAccessShortcutsPermission(
+                injectBinderCallingPid(), injectBinderCallingUid())) {
+            // Verify if caller is the shortcut owner, only if caller doesn't have ACCESS_SHORTCUTS.
+            verifyShortcutInfoPackage(callingPackage, shortcut);
+        }
+        final String shortcutPackage = shortcut.getPackage();
 
         final boolean ret;
         synchronized (mLock) {
@@ -1995,13 +2000,13 @@
             // and then proceed the rest of the process.
             if (shortcut != null) {
                 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(
-                        packageName, userId);
+                        shortcutPackage, userId);
                 final String id = shortcut.getId();
                 if (ps.isShortcutExistsAndInvisibleToPublisher(id)) {
 
                     ps.updateInvisibleShortcutForPinRequestWith(shortcut);
 
-                    packageShortcutsChanged(packageName, userId);
+                    packageShortcutsChanged(shortcutPackage, userId);
                 }
             }
 
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 565a85f..e323c98 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -284,6 +284,10 @@
         return (protectionLevel & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0;
     }
 
+    public boolean isRetailDemo() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0;
+    }
+
     public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
         if (!origPackageName.equals(sourcePackageName)) {
             return;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 6167a50..1fc2dd5 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -55,6 +55,8 @@
 import android.app.ActivityManager;
 import android.app.ApplicationPackageManager;
 import android.app.IActivityManager;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DevicePolicyManagerInternal;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
 import android.content.Context;
@@ -3355,10 +3357,27 @@
                 // Special permissions for the system companion device manager.
                 allowed = true;
             }
+            if (!allowed && bp.isRetailDemo()
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                            PackageManagerInternal.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM),
+                    pkg.getPackageName()) && isProfileOwner(pkg.getUid())) {
+                // Special permission granted only to the OEM specified retail demo app
+                allowed = true;
+            }
         }
         return allowed;
     }
 
+    private static boolean isProfileOwner(int uid) {
+        DevicePolicyManagerInternal dpmInternal =
+                LocalServices.getService(DevicePolicyManagerInternal.class);
+        if (dpmInternal != null) {
+            return dpmInternal
+                    .isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        }
+        return false;
+    }
+
     private static boolean canGrantOemPermission(PackageSetting ps, String permission) {
         if (!ps.isOem()) {
             return false;
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index a86c8d7..2c7795a 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -29,6 +29,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
+import android.app.AppOpsManagerInternal;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -84,6 +85,8 @@
 
     private final Object mLock = new Object();
 
+    private IAppOpsCallback mAppOpsCallback;
+
     /** Whether the user is started but not yet stopped */
     @GuardedBy("mLock")
     private final SparseBooleanArray mIsStarted = new SparseBooleanArray();
@@ -138,7 +141,7 @@
         permManagerInternal.addOnRuntimePermissionStateChangedListener(
                 this::synchronizePackagePermissionsAndAppOpsAsyncForUser);
 
-        IAppOpsCallback appOpsListener = new IAppOpsCallback.Stub() {
+        mAppOpsCallback = new IAppOpsCallback.Stub() {
             public void opChanged(int op, int uid, String packageName) {
                 synchronizePackagePermissionsAndAppOpsAsyncForUser(packageName,
                         UserHandle.getUserId(uid));
@@ -155,7 +158,7 @@
                 PermissionInfo perm = dangerousPerms.get(i);
 
                 if (perm.isRuntime()) {
-                    appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
+                    appOpsService.startWatchingMode(getSwitchOp(perm.name), null, mAppOpsCallback);
                 }
                 if (perm.isSoftRestricted()) {
                     SoftRestrictedPermissionPolicy policy =
@@ -163,7 +166,7 @@
                                     perm.name);
                     int extraAppOp = policy.getExtraAppOpCode();
                     if (extraAppOp != OP_NONE) {
-                        appOpsService.startWatchingMode(extraAppOp, null, appOpsListener);
+                        appOpsService.startWatchingMode(extraAppOp, null, mAppOpsCallback);
                     }
                 }
             }
@@ -386,10 +389,11 @@
      * Synchronizes permission to app ops. You *must* always sync all packages
      * in a shared UID at the same time to ensure proper synchronization.
      */
-    private static class PermissionToOpSynchroniser {
+    private class PermissionToOpSynchroniser {
         private final @NonNull Context mContext;
         private final @NonNull PackageManager mPackageManager;
         private final @NonNull AppOpsManager mAppOpsManager;
+        private final @NonNull AppOpsManagerInternal mAppOpsManagerInternal;
 
         private final @NonNull ArrayMap<String, PermissionInfo> mRuntimePermissionInfos;
 
@@ -429,6 +433,7 @@
             mContext = context;
             mPackageManager = context.getPackageManager();
             mAppOpsManager = context.getSystemService(AppOpsManager.class);
+            mAppOpsManagerInternal = LocalServices.getService(AppOpsManagerInternal.class);
 
             mRuntimePermissionInfos = new ArrayMap<>();
             PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
@@ -668,7 +673,8 @@
                     opCode), uid, packageName);
             if (currentMode != MODE_ALLOWED) {
                 if (currentMode != MODE_IGNORED) {
-                    mAppOpsManager.setUidMode(opCode, uid, MODE_IGNORED);
+                    mAppOpsManagerInternal.setUidModeIgnoringCallback(opCode, uid, MODE_IGNORED,
+                            mAppOpsCallback);
                 }
                 return true;
             }
@@ -680,15 +686,16 @@
             final int oldMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
                     opCode), uid, packageName);
             if (oldMode != mode) {
-                mAppOpsManager.setUidMode(opCode, uid, mode);
+                mAppOpsManagerInternal.setUidModeIgnoringCallback(opCode, uid, mode,
+                        mAppOpsCallback);
                 final int newMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
                         opCode), uid, packageName);
                 if (newMode != mode) {
                     // Work around incorrectly-set package mode. It never makes sense for app ops
                     // related to runtime permissions, but can get in the way and we have to reset
                     // it.
-                    mAppOpsManager.setMode(opCode, uid, packageName, AppOpsManager.opToDefaultMode(
-                            opCode));
+                    mAppOpsManagerInternal.setModeIgnoringCallback(opCode, uid, packageName,
+                            AppOpsManager.opToDefaultMode(opCode), mAppOpsCallback);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 03cb283..ede04f3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -481,6 +481,8 @@
     int mVeryLongPressTimeout;
     boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
     MetricsLogger mLogger;
+    boolean mWakeOnDpadKeyPress;
+    boolean mWakeOnAssistKeyPress;
 
     private boolean mHandleVolumeKeysInWM;
 
@@ -1729,6 +1731,13 @@
         mAccessibilityShortcutController =
                 new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
         mLogger = new MetricsLogger();
+
+        Resources res = mContext.getResources();
+        mWakeOnDpadKeyPress =
+                res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress);
+        mWakeOnAssistKeyPress =
+                res.getBoolean(com.android.internal.R.bool.config_wakeOnAssistKeyPress);
+
         // Init display burn-in protection
         boolean burnInProtectionEnabled = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_enableBurnInProtection);
@@ -4072,13 +4081,11 @@
      */
     private boolean isWakeKeyWhenScreenOff(int keyCode) {
         switch (keyCode) {
-            // ignore volume keys unless docked
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_DOWN:
             case KeyEvent.KEYCODE_VOLUME_MUTE:
                 return mDefaultDisplayPolicy.getDockMode() != Intent.EXTRA_DOCK_STATE_UNDOCKED;
 
-            // ignore media keys
             case KeyEvent.KEYCODE_MUTE:
             case KeyEvent.KEYCODE_HEADSETHOOK:
             case KeyEvent.KEYCODE_MEDIA_PLAY:
@@ -4092,7 +4099,18 @@
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
             case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
                 return false;
+
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+                return mWakeOnDpadKeyPress;
+
+            case KeyEvent.KEYCODE_ASSIST:
+                return mWakeOnAssistKeyPress;
         }
+
         return true;
     }
 
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index eec0d5f..cc72dd6 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -38,9 +38,9 @@
 import android.provider.Settings;
 import android.service.attention.AttentionService;
 import android.util.Slog;
-import android.util.StatsLog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -285,7 +285,8 @@
     private void resetConsecutiveExtensionCount() {
         final long previousCount = mConsecutiveTimeoutExtendedCount.getAndSet(0);
         if (previousCount > 0) {
-            StatsLog.write(StatsLog.SCREEN_TIMEOUT_EXTENSION_REPORTED, previousCount);
+            FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_TIMEOUT_EXTENSION_REPORTED,
+                    previousCount);
         }
     }
 
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index b45522d..0b95be1 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -49,13 +49,13 @@
 import android.telephony.TelephonyManager;
 import android.util.EventLog;
 import android.util.Slog;
-import android.util.StatsLog;
 import android.view.WindowManagerPolicyConstants.OnReason;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -199,8 +199,8 @@
         try {
             mBatteryStats.noteInteractive(true);
         } catch (RemoteException ex) { }
-        StatsLog.write(StatsLog.INTERACTIVE_STATE_CHANGED,
-                StatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON);
+        FrameworkStatsLog.write(FrameworkStatsLog.INTERACTIVE_STATE_CHANGED,
+                FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON);
     }
 
     /**
@@ -247,13 +247,15 @@
         try {
             if (workSource != null) {
                 mBatteryStats.noteLongPartialWakelockStartFromSource(tag, historyTag, workSource);
-                StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, workSource,
-                        tag, historyTag, StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
+                FrameworkStatsLog.write(FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+                        workSource, tag, historyTag,
+                        FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
             } else {
                 mBatteryStats.noteLongPartialWakelockStart(tag, historyTag, ownerUid);
-                StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
-                        ownerUid, null, tag, historyTag,
-                        StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
+                FrameworkStatsLog.write_non_chained(
+                        FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, ownerUid, null, tag,
+                        historyTag,
+                        FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
             }
         } catch (RemoteException ex) {
             // Ignore
@@ -270,13 +272,15 @@
         try {
             if (workSource != null) {
                 mBatteryStats.noteLongPartialWakelockFinishFromSource(tag, historyTag, workSource);
-                StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, workSource,
-                        tag, historyTag, StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
+                FrameworkStatsLog.write(FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+                        workSource, tag, historyTag,
+                        FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
             } else {
                 mBatteryStats.noteLongPartialWakelockFinish(tag, historyTag, ownerUid);
-                StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
-                        ownerUid, null, tag, historyTag,
-                        StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
+                FrameworkStatsLog.write_non_chained(
+                        FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, ownerUid, null, tag,
+                        historyTag,
+                        FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
             }
         } catch (RemoteException ex) {
             // Ignore
@@ -415,9 +419,9 @@
             try {
                 mBatteryStats.noteInteractive(interactive);
             } catch (RemoteException ex) { }
-            StatsLog.write(StatsLog.INTERACTIVE_STATE_CHANGED,
-                    interactive ? StatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON :
-                            StatsLog.INTERACTIVE_STATE_CHANGED__STATE__OFF);
+            FrameworkStatsLog.write(FrameworkStatsLog.INTERACTIVE_STATE_CHANGED,
+                    interactive ? FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON :
+                            FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__OFF);
 
             // Handle early behaviors.
             mInteractive = interactive;
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index e0143ae..bf7413b 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -187,6 +187,14 @@
     private final int[] mPackageSessionIds;
 
     /**
+     * The number of sessions in the install which are notified with success by
+     * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}.
+     * This rollback will be enabled only after all child sessions finished with success.
+     */
+    @GuardedBy("mLock")
+    private int mNumPackageSessionsWithSuccess;
+
+    /**
      * Constructs a new, empty Rollback instance.
      *
      * @param rollbackId the id of the rollback.
@@ -840,6 +848,17 @@
         return mPackageSessionIds.length;
     }
 
+    /**
+     * Called when a child session finished with success.
+     * Returns true when all child sessions are notified with success. This rollback will be
+     * enabled only after all child sessions finished with success.
+     */
+    boolean notifySessionWithSuccess() {
+        synchronized (mLock) {
+            return ++mNumPackageSessionsWithSuccess == mPackageSessionIds.length;
+        }
+    }
+
     static String rollbackStateToString(@RollbackState int state) {
         switch (state) {
             case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 4425c0a..3fa114e 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -123,7 +123,7 @@
 
     // Rollbacks we are in the process of enabling.
     @GuardedBy("mLock")
-    private final Set<NewRollback> mNewRollbacks = new ArraySet<>();
+    private final Set<Rollback> mNewRollbacks = new ArraySet<>();
 
     // The list of all rollbacks, including available and committed rollbacks.
     @GuardedBy("mLock")
@@ -240,16 +240,16 @@
                         Slog.v(TAG, "broadcast=ACTION_CANCEL_ENABLE_ROLLBACK token=" + token);
                     }
                     synchronized (mLock) {
-                        NewRollback found = null;
-                        for (NewRollback newRollback : mNewRollbacks) {
-                            if (newRollback.rollback.hasToken(token)) {
+                        Rollback found = null;
+                        for (Rollback newRollback : mNewRollbacks) {
+                            if (newRollback.hasToken(token)) {
                                 found = newRollback;
                                 break;
                             }
                         }
                         if (found != null) {
                             mNewRollbacks.remove(found);
-                            found.rollback.delete(mAppDataRollbackHelper);
+                            found.delete(mAppDataRollbackHelper);
                         }
                     }
                 }
@@ -442,12 +442,12 @@
                     rollback.delete(mAppDataRollbackHelper);
                 }
             }
-            Iterator<NewRollback> iter2 = mNewRollbacks.iterator();
+            Iterator<Rollback> iter2 = mNewRollbacks.iterator();
             while (iter2.hasNext()) {
-                NewRollback newRollback = iter2.next();
-                if (newRollback.rollback.includesPackage(packageName)) {
+                Rollback newRollback = iter2.next();
+                if (newRollback.includesPackage(packageName)) {
                     iter2.remove();
-                    newRollback.rollback.delete(mAppDataRollbackHelper);
+                    newRollback.delete(mAppDataRollbackHelper);
                 }
 
             }
@@ -802,7 +802,7 @@
             }
         }
 
-        NewRollback newRollback;
+        Rollback newRollback;
         synchronized (mLock) {
             // See if we already have a NewRollback that contains this package
             // session. If not, create a NewRollback for the parent session
@@ -813,9 +813,9 @@
                 mNewRollbacks.add(newRollback);
             }
         }
-        newRollback.rollback.addToken(token);
+        newRollback.addToken(token);
 
-        return enableRollbackForPackageSession(newRollback.rollback, packageSession);
+        return enableRollbackForPackageSession(newRollback, packageSession);
     }
 
     @WorkerThread
@@ -825,12 +825,12 @@
         }
 
         synchronized (mLock) {
-            NewRollback newRollback = getNewRollbackForPackageSessionLocked(sessionId);
+            Rollback newRollback = getNewRollbackForPackageSessionLocked(sessionId);
             if (newRollback != null) {
-                Slog.w(TAG, "Delete new rollback id=" + newRollback.rollback.info.getRollbackId()
+                Slog.w(TAG, "Delete new rollback id=" + newRollback.info.getRollbackId()
                         + " for session id=" + sessionId);
                 mNewRollbacks.remove(newRollback);
-                newRollback.rollback.delete(mAppDataRollbackHelper);
+                newRollback.delete(mAppDataRollbackHelper);
             }
             Iterator<Rollback> iter = mRollbacks.iterator();
             while (iter.hasNext()) {
@@ -972,9 +972,8 @@
                 rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper);
             }
             // non-staged installs
-            for (NewRollback rollback : mNewRollbacks) {
-                rollback.rollback.snapshotUserData(
-                        packageName, userIds, mAppDataRollbackHelper);
+            for (Rollback rollback : mNewRollbacks) {
+                rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper);
             }
         }
     }
@@ -1016,13 +1015,13 @@
                 return;
             }
 
-            NewRollback newRollback;
+            Rollback newRollback;
             synchronized (mLock) {
                 newRollback = createNewRollbackLocked(session);
             }
 
             if (!session.isMultiPackage()) {
-                if (!enableRollbackForPackageSession(newRollback.rollback, session)) {
+                if (!enableRollbackForPackageSession(newRollback, session)) {
                     Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
                     result.offer(-1);
                     return;
@@ -1036,7 +1035,7 @@
                         result.offer(-1);
                         return;
                     }
-                    if (!enableRollbackForPackageSession(newRollback.rollback, childSession)) {
+                    if (!enableRollbackForPackageSession(newRollback, childSession)) {
                         Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
                         result.offer(-1);
                         return;
@@ -1197,7 +1196,7 @@
             }
 
             if (success) {
-                NewRollback newRollback;
+                Rollback newRollback;
                 synchronized (mLock) {
                     newRollback = getNewRollbackForPackageSessionLocked(sessionId);
                     if (newRollback != null && newRollback.notifySessionWithSuccess()) {
@@ -1229,8 +1228,7 @@
      * or null on error.
      */
     @WorkerThread
-    private Rollback completeEnableRollback(NewRollback newRollback) {
-        Rollback rollback = newRollback.rollback;
+    private Rollback completeEnableRollback(Rollback rollback) {
         if (LOCAL_LOGV) {
             Slog.v(TAG, "completeEnableRollback id=" + rollback.info.getRollbackId());
         }
@@ -1341,38 +1339,9 @@
         }
     }
 
-    private static class NewRollback {
-        public final Rollback rollback;
-
-        /**
-         * The number of sessions in the install which are notified with success by
-         * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}.
-         * This NewRollback will be enabled only after all child sessions finished with success.
-         */
-        @GuardedBy("mNewRollbackLock")
-        private int mNumPackageSessionsWithSuccess;
-
-        private final Object mNewRollbackLock = new Object();
-
-        NewRollback(Rollback rollback) {
-            this.rollback = rollback;
-        }
-
-        /**
-         * Called when a child session finished with success.
-         * Returns true when all child sessions are notified with success. This NewRollback will be
-         * enabled only after all child sessions finished with success.
-         */
-        boolean notifySessionWithSuccess() {
-            synchronized (mNewRollbackLock) {
-                return ++mNumPackageSessionsWithSuccess == rollback.getPackageSessionIdCount();
-            }
-        }
-    }
-
     @WorkerThread
     @GuardedBy("mLock")
-    private NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
+    private Rollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
         int rollbackId = allocateRollbackIdLocked();
         final int userId;
         if (parentSession.getUser() == UserHandle.ALL) {
@@ -1404,7 +1373,7 @@
                     installerPackageName, packageSessionIds);
         }
 
-        return new NewRollback(rollback);
+        return rollback;
     }
 
     /**
@@ -1414,11 +1383,11 @@
      */
     @WorkerThread
     @GuardedBy("mLock")
-    NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
+    Rollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
         // We expect mNewRollbacks to be a very small list; linear search
         // should be plenty fast.
-        for (NewRollback newRollback: mNewRollbacks) {
-            if (newRollback.rollback.containsSessionId(packageSessionId)) {
+        for (Rollback newRollback: mNewRollbacks) {
+            if (newRollback.containsSessionId(packageSessionId)) {
                 return newRollback;
             }
         }
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
index 79e1a29..46ec2f8 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -70,20 +70,27 @@
         }
     }
 
+    /**
+     * Returns the logging parent of a given package if it exists, {@code null} otherwise.
+     *
+     * The logging parent is defined by the {@code android.content.pm.LOGGING_PARENT} field in the
+     * metadata of a package's AndroidManifest.xml.
+     */
     @VisibleForTesting
+    @Nullable
     static VersionedPackage getLogPackage(Context context,
             @NonNull VersionedPackage failingPackage) {
         String logPackageName;
         VersionedPackage loggingParent;
         logPackageName = getLoggingParentName(context, failingPackage.getPackageName());
         if (logPackageName == null) {
-            return failingPackage;
+            return null;
         }
         try {
             loggingParent = new VersionedPackage(logPackageName, context.getPackageManager()
                     .getPackageInfo(logPackageName, 0 /* flags */).getLongVersionCode());
         } catch (PackageManager.NameNotFoundException e) {
-            return failingPackage;
+            return null;
         }
         return loggingParent;
     }
@@ -105,18 +112,14 @@
             return;
         }
 
-        // Identify the logging parent for this rollback. When all configurations are correct, each
-        // package in the rollback refers to the same logging parent, except for the logging parent
-        // itself. If a logging parent is missing for a package, we use the package itself for
-        // logging. This might result in over-logging, but we prefer this over no logging.
+        // Identify the logging parent for this rollback. When all configurations are correct,
+        // each package in the rollback has a logging parent set in metadata.
         final Set<String> loggingPackageNames = new ArraySet<>();
         for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
             final String loggingParentName = getLoggingParentName(context,
                     packageRollback.getPackageName());
             if (loggingParentName != null) {
                 loggingPackageNames.add(loggingParentName);
-            } else {
-                loggingPackageNames.add(packageRollback.getPackageName());
             }
         }
 
@@ -168,6 +171,10 @@
         if (logPackage != null) {
             StatsLog.logWatchdogRollbackOccurred(type, logPackage.getPackageName(),
                     logPackage.getVersionCode(), rollbackReason, failingPackageName);
+        } else {
+            // In the case that the log package is null, still log an empty string as an
+            // indication that retrieving the logging parent failed.
+            StatsLog.logWatchdogRollbackOccurred(type, "", 0, rollbackReason, failingPackageName);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index df6dfc4..ed38e9a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3037,6 +3037,8 @@
         }
         // Throw away any services that have been bound by this activity.
         mServiceConnectionsHolder.disconnectActivityFromServices();
+        // This activity record is removing, make sure not to disconnect twice.
+        mServiceConnectionsHolder = null;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 6e75f9c..5dfc261 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -18,10 +18,13 @@
 
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+
+import android.util.ArraySet;
+import android.util.Slog;
 
 import java.io.PrintWriter;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.function.Consumer;
 
 /**
@@ -30,6 +33,8 @@
  * instance of this per activity for tracking all services connected to that activity. AM will
  * sometimes query this to bump the OOM score for the processes with services connected to visible
  * activities.
+ * <p>
+ * Public methods are called in AM lock, otherwise in WM lock.
  */
 public class ActivityServiceConnectionsHolder<T> {
 
@@ -44,7 +49,10 @@
      * on the WM side since we don't perform operations on the object. Mainly here for communication
      * and booking with the AM side.
      */
-    private HashSet<T> mConnections;
+    private ArraySet<T> mConnections;
+
+    /** Whether all connections of {@link #mActivity} are being removed. */
+    private volatile boolean mIsDisconnecting;
 
     ActivityServiceConnectionsHolder(ActivityTaskManagerService service, ActivityRecord activity) {
         mService = service;
@@ -54,8 +62,16 @@
     /** Adds a connection record that the activity has bound to a specific service. */
     public void addConnection(T c) {
         synchronized (mService.mGlobalLock) {
+            if (mIsDisconnecting) {
+                // This is unlikely to happen because the caller should create a new holder.
+                if (DEBUG_CLEANUP) {
+                    Slog.e(TAG_ATM, "Skip adding connection " + c + " to a disconnecting holder of "
+                            + mActivity);
+                }
+                return;
+            }
             if (mConnections == null) {
-                mConnections = new HashSet<>();
+                mConnections = new ArraySet<>();
             }
             mConnections.add(c);
         }
@@ -67,6 +83,9 @@
             if (mConnections == null) {
                 return;
             }
+            if (DEBUG_CLEANUP && mIsDisconnecting) {
+                Slog.v(TAG_ATM, "Remove pending disconnecting " + c + " of " + mActivity);
+            }
             mConnections.remove(c);
         }
     }
@@ -88,26 +107,33 @@
             if (mConnections == null || mConnections.isEmpty()) {
                 return;
             }
-            final Iterator<T> it = mConnections.iterator();
-            while (it.hasNext()) {
-                T c = it.next();
-                consumer.accept(c);
+            for (int i = mConnections.size() - 1; i >= 0; i--) {
+                consumer.accept(mConnections.valueAt(i));
             }
         }
     }
 
-    /** Removes the connection between the activity and all services that were connected to it. */
+    /**
+     * Removes the connection between the activity and all services that were connected to it. In
+     * general, this method is used to clean up if the activity didn't unbind services before it
+     * is destroyed.
+     */
     void disconnectActivityFromServices() {
-        if (mConnections == null || mConnections.isEmpty()) {
+        if (mConnections == null || mConnections.isEmpty() || mIsDisconnecting) {
             return;
         }
-        // Capture and null out mConnections, to guarantee that we process
+        // Mark as disconnecting, to guarantee that we process
         // disconnect of these specific connections exactly once even if
         // we're racing with rapid activity lifecycle churn and this
         // method is invoked more than once on this object.
-        final Object disc = mConnections;
-        mConnections = null;
-        mService.mH.post(() -> mService.mAmInternal.disconnectActivityFromServices(this, disc));
+        // It is possible that {@link #removeConnection} is called while the disconnect-runnable is
+        // still in the message queue, so keep the reference of {@link #mConnections} to make sure
+        // the connection list is up-to-date.
+        mIsDisconnecting = true;
+        mService.mH.post(() -> {
+            mService.mAmInternal.disconnectActivityFromServices(this);
+            mIsDisconnecting = false;
+        });
     }
 
     public void dump(PrintWriter pw, String prefix) {
@@ -116,4 +142,9 @@
         }
     }
 
+    /** Used by {@link ActivityRecord#dump}. */
+    @Override
+    public String toString() {
+        return String.valueOf(mConnections);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 16ac9fb..810aa34 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -194,6 +194,7 @@
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.IntArray;
+import android.util.RotationUtils;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -1603,17 +1604,18 @@
         if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
             return WmDisplayCutout.NO_CUTOUT;
         }
+        final Insets waterfallInsets =
+                RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
         if (rotation == ROTATION_0) {
             return WmDisplayCutout.computeSafeInsets(
                     cutout, mInitialDisplayWidth, mInitialDisplayHeight);
         }
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
         final Rect[] newBounds = mRotationUtil.getRotatedBounds(
-                WmDisplayCutout.computeSafeInsets(
-                        cutout, mInitialDisplayWidth, mInitialDisplayHeight)
-                        .getDisplayCutout().getBoundingRectsAll(),
+                cutout.getBoundingRectsAll(),
                 rotation, mInitialDisplayWidth, mInitialDisplayHeight);
-        return WmDisplayCutout.computeSafeInsets(DisplayCutout.fromBounds(newBounds),
+        return WmDisplayCutout.computeSafeInsets(
+                DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets),
                 rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
                 rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
     }
diff --git a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
index 3be5d31..46fff03 100644
--- a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
+++ b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
@@ -21,7 +21,6 @@
 import android.view.DisplayCutout;
 import android.view.Gravity;
 
-import java.util.List;
 import java.util.Objects;
 
 /**
@@ -41,12 +40,17 @@
         mFrameSize = frameSize;
     }
 
-    public static WmDisplayCutout computeSafeInsets(DisplayCutout inner,
-            int displayWidth, int displayHeight) {
-        if (inner == DisplayCutout.NO_CUTOUT || inner.isBoundsEmpty()) {
+    /**
+     * Compute the safe insets according to the given DisplayCutout and the display size.
+     *
+     * @return return a WmDisplayCutout with calculated safe insets.
+     * @hide
+     */
+    public static WmDisplayCutout computeSafeInsets(
+            DisplayCutout inner, int displayWidth, int displayHeight) {
+        if (inner == DisplayCutout.NO_CUTOUT) {
             return NO_CUTOUT;
         }
-
         final Size displaySize = new Size(displayWidth, displayHeight);
         final Rect safeInsets = computeSafeInsets(displaySize, inner);
         return new WmDisplayCutout(inner.replaceSafeInsets(safeInsets), displaySize);
@@ -112,57 +116,42 @@
     }
 
     private static Rect computeSafeInsets(Size displaySize, DisplayCutout cutout) {
-        if (displaySize.getWidth() < displaySize.getHeight()) {
-            final List<Rect> boundingRects = cutout.replaceSafeInsets(
-                    new Rect(0, displaySize.getHeight() / 2, 0, displaySize.getHeight() / 2))
-                    .getBoundingRects();
-            int topInset = findInsetForSide(displaySize, boundingRects, Gravity.TOP);
-            int bottomInset = findInsetForSide(displaySize, boundingRects, Gravity.BOTTOM);
-            return new Rect(0, topInset, 0, bottomInset);
-        } else if (displaySize.getWidth() > displaySize.getHeight()) {
-            final List<Rect> boundingRects = cutout.replaceSafeInsets(
-                    new Rect(displaySize.getWidth() / 2, 0, displaySize.getWidth() / 2, 0))
-                    .getBoundingRects();
-            int leftInset = findInsetForSide(displaySize, boundingRects, Gravity.LEFT);
-            int right = findInsetForSide(displaySize, boundingRects, Gravity.RIGHT);
-            return new Rect(leftInset, 0, right, 0);
-        } else {
+        if (displaySize.getWidth() == displaySize.getHeight()) {
             throw new UnsupportedOperationException("not implemented: display=" + displaySize +
                     " cutout=" + cutout);
         }
+
+        int leftInset = Math.max(cutout.getWaterfallInsets().left,
+                findCutoutInsetForSide(displaySize, cutout.getBoundingRectLeft(), Gravity.LEFT));
+        int topInset = Math.max(cutout.getWaterfallInsets().top,
+                findCutoutInsetForSide(displaySize, cutout.getBoundingRectTop(), Gravity.TOP));
+        int rightInset = Math.max(cutout.getWaterfallInsets().right,
+                findCutoutInsetForSide(displaySize, cutout.getBoundingRectRight(), Gravity.RIGHT));
+        int bottomInset = Math.max(cutout.getWaterfallInsets().bottom,
+                findCutoutInsetForSide(displaySize, cutout.getBoundingRectBottom(),
+                        Gravity.BOTTOM));
+
+        return new Rect(leftInset, topInset, rightInset, bottomInset);
     }
 
-    private static int findInsetForSide(Size display, List<Rect> boundingRects, int gravity) {
-        int inset = 0;
-        final int size = boundingRects.size();
-        for (int i = 0; i < size; i++) {
-            Rect boundingRect = boundingRects.get(i);
-            switch (gravity) {
-                case Gravity.TOP:
-                    if (boundingRect.top == 0) {
-                        inset = Math.max(inset, boundingRect.bottom);
-                    }
-                    break;
-                case Gravity.BOTTOM:
-                    if (boundingRect.bottom == display.getHeight()) {
-                        inset = Math.max(inset, display.getHeight() - boundingRect.top);
-                    }
-                    break;
-                case Gravity.LEFT:
-                    if (boundingRect.left == 0) {
-                        inset = Math.max(inset, boundingRect.right);
-                    }
-                    break;
-                case Gravity.RIGHT:
-                    if (boundingRect.right == display.getWidth()) {
-                        inset = Math.max(inset, display.getWidth() - boundingRect.left);
-                    }
-                    break;
-                default:
-                    throw new IllegalArgumentException("unknown gravity: " + gravity);
-            }
+    private static int findCutoutInsetForSide(Size display, Rect boundingRect, int gravity) {
+        if (boundingRect.isEmpty()) {
+            return 0;
         }
-        return inset;
+
+        int inset = 0;
+        switch (gravity) {
+            case Gravity.TOP:
+                return Math.max(inset, boundingRect.bottom);
+            case Gravity.BOTTOM:
+                return Math.max(inset, display.getHeight() - boundingRect.top);
+            case Gravity.LEFT:
+                return Math.max(inset, boundingRect.right);
+            case Gravity.RIGHT:
+                return Math.max(inset, display.getWidth() - boundingRect.left);
+            default:
+                throw new IllegalArgumentException("unknown gravity: " + gravity);
+        }
     }
 
     public DisplayCutout getDisplayCutout() {
diff --git a/services/core/jni/com_android_server_incremental_IncrementalManagerService.cpp b/services/core/jni/com_android_server_incremental_IncrementalManagerService.cpp
index 5e255f4..10bac94 100644
--- a/services/core/jni/com_android_server_incremental_IncrementalManagerService.cpp
+++ b/services/core/jni/com_android_server_incremental_IncrementalManagerService.cpp
@@ -33,9 +33,14 @@
     Incremental_IncrementalService_OnSystemReady(self);
 }
 
+static void nativeDump(JNIEnv* env, jclass klass, jlong self, jint fd) {
+    Incremental_IncrementalService_OnDump(self, fd);
+}
+
 static const JNINativeMethod method_table[] = {
         {"nativeStartService", "()J", (void*)nativeStartService},
         {"nativeSystemReady", "(J)V", (void*)nativeSystemReady},
+        {"nativeDump", "(JI)V", (void*)nativeDump},
 };
 
 int register_android_server_incremental_IncrementalManagerService(JNIEnv* env) {
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 91d0572..0941831 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -17,12 +17,14 @@
 #include "BinderIncrementalService.h"
 
 #include <binder/IResultReceiver.h>
+#include <binder/PermissionCache.h>
 #include <incfs.h>
 
 #include "ServiceWrappers.h"
 #include "jni.h"
 #include "nativehelper/JNIHelp.h"
 #include "path.h"
+#include <android-base/logging.h>
 
 using namespace std::literals;
 using namespace android::incremental;
@@ -90,8 +92,13 @@
     return self.get();
 }
 
-status_t BinderIncrementalService::dump(int fd, const Vector<String16>& args) {
-    return OK;
+status_t BinderIncrementalService::dump(int fd, const Vector<String16>&) {
+    static const String16 kDump("android.permission.DUMP");
+    if (!PermissionCache::checkCallingPermission(kDump)) {
+        return PERMISSION_DENIED;
+    }
+    mImpl.onDump(fd);
+    return NO_ERROR;
 }
 
 void BinderIncrementalService::onSystemReady() {
@@ -280,3 +287,10 @@
         ((android::os::incremental::BinderIncrementalService*)self)->onSystemReady();
     }
 }
+void Incremental_IncrementalService_OnDump(jlong self, jint fd) {
+    if (self) {
+        ((android::os::incremental::BinderIncrementalService*)self)->dump(fd, {});
+    } else {
+        dprintf(fd, "BinderIncrementalService is stopped.");
+    }
+}
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index e4a37dde..dbd97cf 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -35,6 +35,7 @@
 #include <uuid/uuid.h>
 #include <zlib.h>
 
+#include <ctime>
 #include <iterator>
 #include <span>
 #include <stack>
@@ -169,7 +170,7 @@
         base::StringAppendF(&name, "%.*s_%d_%d", int(constants().storagePrefix.size()),
                             constants().storagePrefix.data(), id, no);
         auto fullName = path::join(root, constants().mount, name);
-        if (auto err = incrementalService.mIncFs->makeDir(control, fullName); !err) {
+        if (auto err = incrementalService.mIncFs->makeDir(control, fullName, 0755); !err) {
             std::lock_guard l(lock);
             return storages.insert_or_assign(id, Storage{std::move(fullName)}).first;
         } else if (err != EEXIST) {
@@ -256,6 +257,68 @@
 
 IncrementalService::~IncrementalService() = default;
 
+inline const char* toString(TimePoint t) {
+    using SystemClock = std::chrono::system_clock;
+    time_t time = SystemClock::to_time_t(SystemClock::now() + std::chrono::duration_cast<SystemClock::duration>(t - Clock::now()));
+    return std::ctime(&time);
+}
+
+inline const char* toString(IncrementalService::BindKind kind) {
+    switch (kind) {
+    case IncrementalService::BindKind::Temporary:
+        return "Temporary";
+    case IncrementalService::BindKind::Permanent:
+        return "Permanent";
+    }
+}
+
+void IncrementalService::onDump(int fd) {
+    dprintf(fd, "Incremental is %s\n", incfs::enabled() ? "ENABLED" : "DISABLED");
+    dprintf(fd, "Incremental dir: %s\n", mIncrementalDir.c_str());
+
+    std::unique_lock l(mLock);
+
+    dprintf(fd, "Mounts (%d):\n", int(mMounts.size()));
+    for (auto&& [id, ifs] : mMounts) {
+        const IncFsMount& mnt = *ifs.get();
+        dprintf(fd, "\t[%d]:\n", id);
+        dprintf(fd, "\t\tmountId: %d\n", mnt.mountId);
+        dprintf(fd, "\t\tnextStorageDirNo: %d\n", mnt.nextStorageDirNo.load());
+        dprintf(fd, "\t\tdataLoaderStatus: %d\n", mnt.dataLoaderStatus.load());
+        dprintf(fd, "\t\tconnectionLostTime: %s\n", toString(mnt.connectionLostTime));
+        if (mnt.savedDataLoaderParams) {
+            const auto& params = mnt.savedDataLoaderParams.value();
+            dprintf(fd, "\t\tsavedDataLoaderParams:\n");
+            dprintf(fd, "\t\t\ttype: %s\n", toString(params.type).c_str());
+            dprintf(fd, "\t\t\tpackageName: %s\n", params.packageName.c_str());
+            dprintf(fd, "\t\t\tclassName: %s\n", params.className.c_str());
+            dprintf(fd, "\t\t\targuments: %s\n", params.arguments.c_str());
+            dprintf(fd, "\t\t\tdynamicArgs: %d\n", int(params.dynamicArgs.size()));
+        }
+        dprintf(fd, "\t\tstorages (%d):\n", int(mnt.storages.size()));
+        for (auto&& [storageId, storage] : mnt.storages) {
+            dprintf(fd, "\t\t\t[%d] -> [%s]\n", storageId, storage.name.c_str());
+        }
+
+        dprintf(fd, "\t\tbindPoints (%d):\n", int(mnt.bindPoints.size()));
+        for (auto&& [target, bind] : mnt.bindPoints) {
+            dprintf(fd, "\t\t\t[%s]->[%d]:\n", target.c_str(), bind.storage);
+            dprintf(fd, "\t\t\t\tsavedFilename: %s\n", bind.savedFilename.c_str());
+            dprintf(fd, "\t\t\t\tsourceDir: %s\n", bind.sourceDir.c_str());
+            dprintf(fd, "\t\t\t\tkind: %s\n", toString(bind.kind));
+        }
+    }
+
+    dprintf(fd, "Sorted binds (%d):\n", int(mBindsByPath.size()));
+    for (auto&& [target, mountPairIt] : mBindsByPath) {
+        const auto& bind = mountPairIt->second;
+        dprintf(fd, "\t\t[%s]->[%d]:\n", target.c_str(), bind.storage);
+        dprintf(fd, "\t\t\tsavedFilename: %s\n", bind.savedFilename.c_str());
+        dprintf(fd, "\t\t\tsourceDir: %s\n", bind.sourceDir.c_str());
+        dprintf(fd, "\t\t\tkind: %s\n", toString(bind.kind));
+    }
+}
+
 std::optional<std::future<void>> IncrementalService::onSystemReady() {
     std::promise<void> threadFinished;
     if (mSystemReady.exchange(true)) {
@@ -617,15 +680,7 @@
     if (storageInfo == ifs->storages.end()) {
         return -EINVAL;
     }
-    std::string normSource;
-    if (path::isAbsolute(source)) {
-        normSource = path::normalize(source);
-    } else {
-        normSource = path::normalize(path::join(storageInfo->second.name, source));
-    }
-    if (!path::startsWith(normSource, storageInfo->second.name)) {
-        return -EINVAL;
-    }
+    std::string normSource = normalizePathToStorage(ifs, storage, source);
     l.unlock();
     std::unique_lock l2(mLock, std::defer_lock);
     return addBindMount(*ifs, storage, storageInfo->second.name, std::move(normSource),
@@ -674,20 +729,29 @@
     return 0;
 }
 
+std::string IncrementalService::normalizePathToStorage(const IncrementalService::IfsMountPtr ifs,
+                                                       StorageId storage, std::string_view path) {
+    const auto storageInfo = ifs->storages.find(storage);
+    if (storageInfo == ifs->storages.end()) {
+        return {};
+    }
+    std::string normPath;
+    if (path::isAbsolute(path)) {
+        normPath = path::normalize(path);
+    } else {
+        normPath = path::normalize(path::join(storageInfo->second.name, path));
+    }
+    if (!path::startsWith(normPath, storageInfo->second.name)) {
+        return {};
+    }
+    return normPath;
+}
+
 int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
                                  incfs::NewFileParams params) {
     if (auto ifs = getIfs(storage)) {
-        const auto storageInfo = ifs->storages.find(storage);
-        if (storageInfo == ifs->storages.end()) {
-            return -EINVAL;
-        }
-        std::string normPath;
-        if (path::isAbsolute(path)) {
-            normPath = path::normalize(path);
-        } else {
-            normPath = path::normalize(path::join(storageInfo->second.name, path));
-        }
-        if (!path::startsWith(normPath, storageInfo->second.name)) {
+        std::string normPath = normalizePathToStorage(ifs, storage, path);
+        if (normPath.empty()) {
             return -EINVAL;
         }
         auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params);
@@ -706,7 +770,11 @@
 
 int IncrementalService::makeDir(StorageId storageId, std::string_view path, int mode) {
     if (auto ifs = getIfs(storageId)) {
-        return mIncFs->makeDir(ifs->control, path, mode);
+        std::string normPath = normalizePathToStorage(ifs, storageId, path);
+        if (normPath.empty()) {
+            return -EINVAL;
+        }
+        return mIncFs->makeDir(ifs->control, normPath, mode);
     }
     return -EINVAL;
 }
@@ -716,31 +784,40 @@
     if (!ifs) {
         return -EINVAL;
     }
-
-    auto err = mIncFs->makeDir(ifs->control, path, mode);
+    std::string normPath = normalizePathToStorage(ifs, storageId, path);
+    if (normPath.empty()) {
+        return -EINVAL;
+    }
+    auto err = mIncFs->makeDir(ifs->control, normPath, mode);
     if (err == -EEXIST) {
         return 0;
     } else if (err != -ENOENT) {
         return err;
     }
-    if (auto err = makeDirs(storageId, path::dirname(path), mode)) {
+    if (auto err = makeDirs(storageId, path::dirname(normPath), mode)) {
         return err;
     }
-    return mIncFs->makeDir(ifs->control, path, mode);
+    return mIncFs->makeDir(ifs->control, normPath, mode);
 }
 
 int IncrementalService::link(StorageId sourceStorageId, std::string_view oldPath,
                              StorageId destStorageId, std::string_view newPath) {
     if (auto ifsSrc = getIfs(sourceStorageId), ifsDest = getIfs(destStorageId);
         ifsSrc && ifsSrc == ifsDest) {
-        return mIncFs->link(ifsSrc->control, oldPath, newPath);
+        std::string normOldPath = normalizePathToStorage(ifsSrc, sourceStorageId, oldPath);
+        std::string normNewPath = normalizePathToStorage(ifsDest, destStorageId, newPath);
+        if (normOldPath.empty() || normNewPath.empty()) {
+            return -EINVAL;
+        }
+        return mIncFs->link(ifsSrc->control, normOldPath, normNewPath);
     }
     return -EINVAL;
 }
 
 int IncrementalService::unlink(StorageId storage, std::string_view path) {
     if (auto ifs = getIfs(storage)) {
-        return mIncFs->unlink(ifs->control, path);
+        std::string normOldPath = normalizePathToStorage(ifs, storage, path);
+        return mIncFs->unlink(ifs->control, normOldPath);
     }
     return -EINVAL;
 }
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index ca5e4db..dec9f64 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -18,8 +18,8 @@
 
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <android/os/incremental/IIncrementalManager.h>
 #include <android/content/pm/DataLoaderParamsParcel.h>
+#include <android/os/incremental/IIncrementalManager.h>
 #include <binder/IServiceManager.h>
 #include <utils/String16.h>
 #include <utils/StrongPointer.h>
@@ -90,10 +90,11 @@
         return idFromMetadata({(const uint8_t*)metadata.data(), metadata.size()});
     }
 
+    void onDump(int fd);
+
     std::optional<std::future<void>> onSystemReady();
 
-    StorageId createStorage(std::string_view mountPoint,
-                            DataLoaderParamsParcel&& dataLoaderParams,
+    StorageId createStorage(std::string_view mountPoint, DataLoaderParamsParcel&& dataLoaderParams,
                             CreateOptions options = CreateOptions::Default);
     StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage,
                                   CreateOptions options = CreateOptions::Default);
@@ -109,8 +110,8 @@
 
     int makeFile(StorageId storage, std::string_view path, int mode, FileId id,
                  incfs::NewFileParams params);
-    int makeDir(StorageId storage, std::string_view path, int mode = 0555);
-    int makeDirs(StorageId storage, std::string_view path, int mode = 0555);
+    int makeDir(StorageId storage, std::string_view path, int mode = 0755);
+    int makeDirs(StorageId storage, std::string_view path, int mode = 0755);
 
     int link(StorageId sourceStorageId, std::string_view oldPath, StorageId destStorageId,
              std::string_view newPath);
@@ -207,6 +208,8 @@
     void deleteStorage(IncFsMount& ifs);
     void deleteStorageLocked(IncFsMount& ifs, std::unique_lock<std::mutex>&& ifsLock);
     MountMap::iterator getStorageSlotLocked();
+    std::string normalizePathToStorage(const IfsMountPtr incfs, StorageId storage,
+                                       std::string_view path);
 
     // Member variables
     std::unique_ptr<VoldServiceWrapper> mVold;
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index ae3739d..f0b5672 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -70,7 +70,7 @@
     virtual ~IncFsWrapper() = default;
     virtual ErrorCode makeFile(Control control, std::string_view path, int mode, FileId id,
                                NewFileParams params) const = 0;
-    virtual ErrorCode makeDir(Control control, std::string_view path, int mode = 0555) const = 0;
+    virtual ErrorCode makeDir(Control control, std::string_view path, int mode) const = 0;
     virtual RawMetadata getMetadata(Control control, FileId fileid) const = 0;
     virtual RawMetadata getMetadata(Control control, std::string_view path) const = 0;
     virtual FileId getFileId(Control control, std::string_view path) const = 0;
diff --git a/services/incremental/include/incremental_service.h b/services/incremental/include/incremental_service.h
index 7109d95..4a34b11 100644
--- a/services/incremental/include/incremental_service.h
+++ b/services/incremental/include/incremental_service.h
@@ -26,6 +26,7 @@
 
 jlong Incremental_IncrementalService_Start();
 void Incremental_IncrementalService_OnSystemReady(jlong self);
+void Incremental_IncrementalService_OnDump(jlong self, jint fd);
 
 __END_DECLS
 
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 2826818..9cdc83e 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -406,29 +406,16 @@
     int storageId =
             mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
                                                IncrementalService::CreateOptions::CreateNew);
-    std::string_view dir_path("test");
-    EXPECT_CALL(*mIncFs, makeDir(_, dir_path, _));
-    auto res = mIncrementalService->makeDir(storageId, dir_path, 0555);
-    ASSERT_EQ(res, 0);
-}
+    std::string dir_path("test");
 
-TEST_F(IncrementalServiceTest, testMakeDirectoryNested) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mVold->bindMountSuccess();
-    mIncrementalManager->prepareDataLoaderSuccess();
-    mIncrementalManager->startDataLoaderSuccess();
-    TemporaryDir tempDir;
-    int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                               IncrementalService::CreateOptions::CreateNew);
-    auto first = "first"sv;
-    auto second = "second"sv;
-    std::string dir_path = std::string(first) + "/" + std::string(second);
-    EXPECT_CALL(*mIncFs, makeDir(_, first, _)).Times(0);
-    EXPECT_CALL(*mIncFs, makeDir(_, second, _)).Times(0);
-    EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(dir_path), _)).Times(1);
+    std::string tempPath(tempDir.path);
+    std::replace(tempPath.begin(), tempPath.end(), '/', '_');
+    std::string mount_dir = std::string(mRootDir.path) + "/" + tempPath.substr(1);
+    std::string normalized_dir_path = mount_dir + "/mount/st_1_0/" + dir_path;
 
+    // Expecting incfs to call makeDir on a path like:
+    // /data/local/tmp/TemporaryDir-06yixG/data_local_tmp_TemporaryDir-xwdFhT/mount/st_1_0/test
+    EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_dir_path), _));
     auto res = mIncrementalService->makeDir(storageId, dir_path, 0555);
     ASSERT_EQ(res, 0);
 }
@@ -446,15 +433,29 @@
     auto first = "first"sv;
     auto second = "second"sv;
     auto third = "third"sv;
+
+    std::string tempPath(tempDir.path);
+    std::replace(tempPath.begin(), tempPath.end(), '/', '_');
+    std::string mount_dir = std::string(mRootDir.path) + "/" + tempPath.substr(1);
+
     InSequence seq;
     auto parent_path = std::string(first) + "/" + std::string(second);
     auto dir_path = parent_path + "/" + std::string(third);
-    EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(dir_path), _)).WillOnce(Return(-ENOENT));
-    EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(parent_path), _)).WillOnce(Return(-ENOENT));
-    EXPECT_CALL(*mIncFs, makeDir(_, first, _)).WillOnce(Return(0));
-    EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(parent_path), _)).WillOnce(Return(0));
-    EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(dir_path), _)).WillOnce(Return(0));
-    auto res = mIncrementalService->makeDirs(storageId, dir_path, 0555);
+
+    std::string normalized_first_path = mount_dir + "/mount/st_1_0/" + std::string(first);
+    std::string normalized_parent_path = mount_dir + "/mount/st_1_0/" + parent_path;
+    std::string normalized_dir_path = mount_dir + "/mount/st_1_0/" + dir_path;
+
+    EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_dir_path), _))
+            .WillOnce(Return(-ENOENT));
+    EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_parent_path), _))
+            .WillOnce(Return(-ENOENT));
+    EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_first_path), _))
+            .WillOnce(Return(0));
+    EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_parent_path), _))
+            .WillOnce(Return(0));
+    EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_dir_path), _)).WillOnce(Return(0));
+    auto res = mIncrementalService->makeDirs(storageId, normalized_dir_path, 0555);
     ASSERT_EQ(res, 0);
 }
 } // namespace android::os::incremental
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 1985513..e5ec1f7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -36,6 +36,7 @@
 import com.android.server.testables.TestableDeviceConfig;
 
 import org.junit.After;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -122,6 +123,8 @@
                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
         assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+        assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
+                CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
         assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
@@ -132,6 +135,8 @@
                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
         assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
+                CachedAppOptimizer.DEFAULT_USE_FREEZER);
 
         Set<Integer> expected = new HashSet<>();
         for (String s : TextUtils.split(
@@ -177,6 +182,9 @@
                 CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
                 Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE,
+                Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB,
                 Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -185,6 +193,11 @@
                         CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
+                CachedAppOptimizer.DEFAULT_USE_FREEZER);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_USE_FREEZER, CachedAppOptimizer.DEFAULT_USE_FREEZER
+                ?  "false" : "true" , false);
 
         // Then calling init will read and set that flag.
         mCachedAppOptimizerUnderTest.init();
@@ -211,6 +224,8 @@
                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
         assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
+        assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
+                CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
@@ -218,6 +233,14 @@
         assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
+
+        if (mCachedAppOptimizerUnderTest.isFreezerSupported()) {
+            if (CachedAppOptimizer.DEFAULT_USE_FREEZER) {
+                assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
+            } else {
+                assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue();
+            }
+        }
     }
 
     @Test
@@ -244,6 +267,44 @@
     }
 
     @Test
+    public void useFreeze_doesNotListenToDeviceConfigChanges() throws InterruptedException {
+        Assume.assumeTrue(mCachedAppOptimizerUnderTest.isFreezerSupported());
+
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
+                CachedAppOptimizer.DEFAULT_USE_FREEZER);
+
+        // The freezer DeviceConfig property is read at boot only
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_USE_FREEZER, "true", false);
+        mCachedAppOptimizerUnderTest.init();
+        mCountDown = new CountDownLatch(1);
+
+        // No notifications should get to the cached app optimizer.
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isFalse();
+
+        // The flag value has to be set correctly.
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue();
+        // The cached app optimizer thread must be running.
+        assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();
+
+        // Set the flag the other way without rebooting. It shall not change.
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_USE_FREEZER, "false", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue();
+
+
+        // Now, set the flag to false and restart the cached app optimizer
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_USE_FREEZER, "false", false);
+        mCachedAppOptimizerUnderTest.init();
+
+        // The flag value has to be set correctly.
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
+    }
+
+    @Test
     public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
         assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
                 CachedAppOptimizer.DEFAULT_USE_COMPACTION);
@@ -261,6 +322,22 @@
     }
 
     @Test
+    public void useFreeze_listensToDeviceConfigChangesBadValues() throws InterruptedException {
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
+                CachedAppOptimizer.DEFAULT_USE_FREEZER);
+
+        // When we push an invalid flag value...
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_USE_FREEZER, "foobar", false);
+
+        mCachedAppOptimizerUnderTest.init();
+
+        // Then we set the default.
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
+                CachedAppOptimizer.DEFAULT_USE_FREEZER);
+    }
+
+    @Test
     public void compactAction_listensToDeviceConfigChanges() throws InterruptedException {
         mCachedAppOptimizerUnderTest.init();
 
@@ -482,6 +559,17 @@
         // Then that override is reflected in the compactor.
         assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
+
+        // When we override mFreezerStatsdSampleRate with a reasonable value ...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE,
+                Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that override is reflected in the compactor.
+        assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
+                CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
     }
 
     @Test
@@ -498,6 +586,16 @@
         // Then that override is reflected in the compactor.
         assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+
+        // When we override mFreezerStatsdSampleRate with an unreasonable value ...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that override is reflected in the freezer.
+        assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
+                CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
     }
 
     @Test
@@ -523,6 +621,25 @@
 
         // Then the values is capped in the range.
         assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(1.0f);
+
+        // When we override mFreezerStatsdSampleRate with an value outside of [0..1]...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE,
+                Float.toString(-1.0f), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then the values is capped in the range.
+        assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(0.0f);
+
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE,
+                Float.toString(1.01f), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then the values is capped in the range.
+        assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(1.0f);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index fd8ccb3..4a7636a 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -31,6 +31,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
@@ -109,14 +110,21 @@
     private static final String PLAY_STORE_CERT = "play_store_cert";
     private static final String ADB_CERT = "";
 
-    @org.junit.Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @org.junit.Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
-    @Mock PackageManagerInternal mPackageManagerInternal;
-    @Mock Context mMockContext;
-    @Mock Resources mMockResources;
-    @Mock RuleEvaluationEngine mRuleEvaluationEngine;
-    @Mock IntegrityFileManager mIntegrityFileManager;
-    @Mock Handler mHandler;
+    @Mock
+    PackageManagerInternal mPackageManagerInternal;
+    @Mock
+    Context mMockContext;
+    @Mock
+    Resources mMockResources;
+    @Mock
+    RuleEvaluationEngine mRuleEvaluationEngine;
+    @Mock
+    IntegrityFileManager mIntegrityFileManager;
+    @Mock
+    Handler mHandler;
 
     private PackageManager mSpyPackageManager;
     private File mTestApk;
@@ -142,13 +150,10 @@
         // setup mocks to prevent NPE
         when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
         when(mMockContext.getResources()).thenReturn(mMockResources);
-        when(mMockResources.getStringArray(anyInt())).thenReturn(new String[] {});
+        when(mMockResources.getStringArray(anyInt())).thenReturn(new String[]{});
         when(mIntegrityFileManager.initialized()).thenReturn(true);
     }
 
-    // TODO(b/148370598): Implement a test to validate that allow response is retuned when the test
-    //    request times out.
-
     @Test
     public void updateRuleSet_notAuthorized() throws Exception {
         makeUsSystemApp();
@@ -357,9 +362,17 @@
     public void verifierAsInstaller_skipIntegrityVerification() throws Exception {
         whitelistUsAsRuleProvider();
         makeUsSystemApp();
+        mService =
+                new AppIntegrityManagerServiceImpl(
+                        mMockContext,
+                        mPackageManagerInternal,
+                        mRuleEvaluationEngine,
+                        mIntegrityFileManager,
+                        mHandler,
+                        /* checkIntegrityForRuleProviders= */ false);
         ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mMockContext)
+        verify(mMockContext, atLeastOnce())
                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
         Intent intent = makeVerificationIntent(TEST_FRAMEWORK_PACKAGE);
         when(mRuleEvaluationEngine.evaluate(any(), any()))
@@ -386,7 +399,7 @@
     private void whitelistUsAsRuleProvider() {
         Resources mockResources = mock(Resources.class);
         when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
-                .thenReturn(new String[] {TEST_FRAMEWORK_PACKAGE});
+                .thenReturn(new String[]{TEST_FRAMEWORK_PACKAGE});
         when(mMockContext.getResources()).thenReturn(mockResources);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index 164c883..9719509 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -291,6 +291,17 @@
         verify(mMockDataHelper).restoreAppData(123, pkgInfo1, 7, 333, "blah");
     }
 
+    @Test
+    public void notifySessionWithSuccess() {
+        int[] sessionIds = new int[]{ 7777, 8888 };
+        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
+                sessionIds);
+        // The 1st invocation returns false because not all child sessions are notified.
+        assertThat(rollback.notifySessionWithSuccess()).isFalse();
+        // The 2nd invocation returns true because now all child sessions are notified.
+        assertThat(rollback.notifySessionWithSuccess()).isTrue();
+    }
+
     private static PackageRollbackInfo newPkgInfoFor(
             String packageName, long fromVersion, long toVersion, boolean isApex) {
         return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),
diff --git a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
index 61117f1..ba493d4 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
@@ -60,18 +60,18 @@
     }
 
     /**
-     * Ensures that the original package is returned if the application info has no metadata.
+     * Ensures that null is returned if the application info has no metadata.
      */
     @Test
     public void testLogPackageHasNoMetadata() throws Exception {
         when(mMockPm.getApplicationInfo(anyString(), anyInt())).thenReturn(mApplicationInfo);
         VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
                 sTestPackageV1);
-        assertThat(logPackage).isEqualTo(sTestPackageV1);
+        assertThat(logPackage).isNull();
     }
 
     /**
-     * Ensures the original package is returned if the application info does not contain a logging
+     * Ensures that null is returned if the application info does not contain a logging
      * parent key.
      */
     @Test
@@ -81,7 +81,7 @@
         bundle.putString(LOGGING_PARENT_KEY, null);
         VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
                 sTestPackageV1);
-        assertThat(logPackage).isEqualTo(sTestPackageV1);
+        assertThat(logPackage).isNull();
     }
 
     /**
@@ -102,8 +102,7 @@
     }
 
     /**
-     * Ensures that the original package is returned if Package Manager does not know about the
-     * logging parent.
+     * Ensures that null is returned if Package Manager does not know about the logging parent.
      */
     @Test
     public void testLogPackageNameNotFound() throws Exception {
@@ -114,6 +113,6 @@
                 new PackageManager.NameNotFoundException());
         VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
                 sTestPackageV1);
-        assertThat(logPackage).isEqualTo(sTestPackageV1);
+        assertThat(logPackage).isNull();
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 2cf5eae..34872db 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -159,6 +159,7 @@
 import com.android.server.lights.LogicalLight;
 import com.android.server.notification.NotificationManagerService.NotificationAssistants;
 import com.android.server.notification.NotificationManagerService.NotificationListeners;
+import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -261,6 +262,8 @@
     NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
     private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
             1 << 30);
+    @Mock
+    StatusBarManagerInternal mStatusBar;
 
     // Use a Testable subclass so we can simulate calls from the system without failing.
     private static class TestableNotificationManagerService extends NotificationManagerService {
@@ -356,6 +359,8 @@
         LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
         LocalServices.removeServiceForTest(WindowManagerInternal.class);
         LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
+        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+        LocalServices.addService(StatusBarManagerInternal.class, mStatusBar);
 
         doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
 
@@ -4483,7 +4488,7 @@
     }
 
     @Test
-    public void testAllowForegroundToasts() throws Exception {
+    public void testAllowForegroundCustomToasts() throws Exception {
         final String testPackage = "testPackageName";
         assertEquals(0, mService.mToastQueue.size());
         mService.isSystemUid = false;
@@ -4506,6 +4511,79 @@
     }
 
     @Test
+    public void testDisallowBackgroundCustomToasts() throws Exception {
+        final String testPackage = "testPackageName";
+        assertEquals(0, mService.mToastQueue.size());
+        mService.isSystemUid = false;
+
+        // package is not suspended
+        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+                .thenReturn(false);
+
+        // this app is NOT in the foreground
+        when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_NONE);
+
+        // enqueue toast -> no toasts enqueued
+        ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
+                new TestableToastCallback(), 2000, 0);
+        assertEquals(0, mService.mToastQueue.size());
+    }
+
+    @Test
+    public void testAllowForegroundTextToasts() throws Exception {
+        final String testPackage = "testPackageName";
+        assertEquals(0, mService.mToastQueue.size());
+        mService.isSystemUid = false;
+
+        // package is not suspended
+        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+                .thenReturn(false);
+
+        // this app is in the foreground
+        when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_FOREGROUND);
+
+        // enqueue toast -> toast should still enqueue
+        ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
+                "Text", 2000, 0, null);
+        assertEquals(1, mService.mToastQueue.size());
+    }
+
+    @Test
+    public void testAllowBackgroundTextToasts() throws Exception {
+        final String testPackage = "testPackageName";
+        assertEquals(0, mService.mToastQueue.size());
+        mService.isSystemUid = false;
+
+        // package is not suspended
+        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+                .thenReturn(false);
+
+        // this app is NOT in the foreground
+        when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_NONE);
+
+        // enqueue toast -> toast should still enqueue
+        ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
+                "Text", 2000, 0, null);
+        assertEquals(1, mService.mToastQueue.size());
+    }
+
+    @Test
+    public void testTextToastsCallStatusBar() throws Exception {
+        final String testPackage = "testPackageName";
+        assertEquals(0, mService.mToastQueue.size());
+        mService.isSystemUid = false;
+
+        // package is not suspended
+        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+                .thenReturn(false);
+
+        // enqueue toast -> no toasts enqueued
+        ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
+                "Text", 2000, 0, null);
+        verify(mStatusBar).showToast(any(), any(), any(), any(), anyInt(), any());
+    }
+
+    @Test
     public void testDisallowToastsFromSuspendedPackages() throws Exception {
         final String testPackage = "testPackageName";
         assertEquals(0, mService.mToastQueue.size());
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
index fb8ba7b..a283476 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
@@ -47,6 +47,7 @@
 @SmallTest
 @Presubmit
 public class WmDisplayCutoutTest {
+    private static final Rect ZERO_RECT = new Rect();
     private final DisplayCutout mCutoutTop = new DisplayCutout(
             Insets.of(0, 100, 0, 0),
             null /* boundLeft */, new Rect(50, 0, 75, 100) /* boundTop */,
@@ -99,41 +100,204 @@
     }
 
     @Test
-    public void computeSafeInsets_top() {
+    public void computeSafeInsets_cutoutTop() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400);
+                fromBoundingRect(80, 0, 120, 20, BOUNDS_POSITION_TOP), 200, 400);
 
         assertEquals(new Rect(0, 20, 0, 0), cutout.getDisplayCutout().getSafeInsets());
     }
 
     @Test
-    public void computeSafeInsets_left() {
+    public void computeSafeInsets_cutoutLeft() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 20, 100, BOUNDS_POSITION_LEFT), 400, 200);
+                fromBoundingRect(0, 180, 20, 220, BOUNDS_POSITION_LEFT), 200, 400);
 
         assertEquals(new Rect(20, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
     }
 
     @Test
-    public void computeSafeInsets_bottom() {
+    public void computeSafeInsets_cutoutBottom() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 180, 100, 200, BOUNDS_POSITION_BOTTOM), 100, 200);
+                fromBoundingRect(80, 380, 120, 400, BOUNDS_POSITION_BOTTOM), 200, 400);
 
         assertEquals(new Rect(0, 0, 0, 20), cutout.getDisplayCutout().getSafeInsets());
     }
 
     @Test
-    public void computeSafeInsets_right() {
+    public void computeSafeInsets_cutoutRight() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(180, 0, 200, 100, BOUNDS_POSITION_RIGHT), 200, 100);
+                fromBoundingRect(180, 180, 200, 220, BOUNDS_POSITION_RIGHT), 200, 400);
 
         assertEquals(new Rect(0, 0, 20, 0), cutout.getDisplayCutout().getSafeInsets());
     }
 
     @Test
+    public void computeSafeInsets_topLeftCornerCutout_portrait() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(0, 0, 20, 20, BOUNDS_POSITION_TOP), 200, 400);
+
+        assertEquals(new Rect(0, 20, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_topRightCornerCutout_portrait() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(180, 0, 200, 20, BOUNDS_POSITION_TOP), 200, 400);
+
+        assertEquals(new Rect(0, 20, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_bottomLeftCornerCutout_portrait() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(0, 380, 20, 400, BOUNDS_POSITION_BOTTOM), 200, 400);
+
+        assertEquals(new Rect(0, 0, 0, 20), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_bottomRightCornerCutout_portrait() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(180, 380, 200, 400, BOUNDS_POSITION_BOTTOM), 200, 400);
+
+        assertEquals(new Rect(0, 0, 0, 20), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_topLeftCornerCutout_landscape() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(0, 0, 20, 20, BOUNDS_POSITION_LEFT), 400, 200);
+
+        assertEquals(new Rect(20, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_topRightCornerCutout_landscape() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(380, 0, 400, 20, BOUNDS_POSITION_RIGHT), 400, 200);
+
+        assertEquals(new Rect(0, 0, 20, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_bottomLeftCornerCutout_landscape() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(0, 180, 20, 200, BOUNDS_POSITION_LEFT), 400, 200);
+
+        assertEquals(new Rect(20, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_bottomRightCornerCutout_landscape() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(380, 180, 400, 200, BOUNDS_POSITION_RIGHT), 400, 200);
+
+        assertEquals(new Rect(0, 0, 20, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_waterfall() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                DisplayCutout.fromBoundsAndWaterfall(
+                        new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT},
+                        Insets.of(1, 2, 3, 4)),
+                200, 400);
+
+        assertEquals(new Rect(1, 2, 3, 4), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_cutoutTop_greaterThan_waterfallTop() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                DisplayCutout.fromBoundsAndWaterfall(
+                        new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT},
+                        Insets.of(0, 20, 0, 0)),
+                200, 400);
+
+        assertEquals(new Rect(0, 30, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_cutoutTop_lessThan_waterfallTop() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                DisplayCutout.fromBoundsAndWaterfall(
+                        new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT},
+                        Insets.of(0, 40, 0, 0)),
+                200, 400);
+
+        assertEquals(new Rect(0, 40, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_cutoutLeft_greaterThan_waterfallLeft() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                DisplayCutout.fromBoundsAndWaterfall(
+                        new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT},
+                        Insets.of(20, 0, 0, 0)),
+                200, 400);
+
+        assertEquals(new Rect(30, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_cutoutLeft_lessThan_waterfallLeft() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                DisplayCutout.fromBoundsAndWaterfall(
+                        new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT},
+                        Insets.of(40, 0, 0, 0)),
+                200, 400);
+
+        assertEquals(new Rect(40, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_cutoutBottom_greaterThan_waterfallBottom() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                DisplayCutout.fromBoundsAndWaterfall(
+                        new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)},
+                        Insets.of(0, 0, 0, 20)),
+                200, 400);
+
+        assertEquals(new Rect(0, 0, 0, 30), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_cutoutBottom_lessThan_waterfallBottom() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                DisplayCutout.fromBoundsAndWaterfall(
+                        new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)},
+                        Insets.of(0, 0, 0, 40)),
+                200, 400);
+
+        assertEquals(new Rect(0, 0, 0, 40), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_cutoutRight_greaterThan_waterfallRight() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                DisplayCutout.fromBoundsAndWaterfall(
+                        new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT},
+                        Insets.of(0, 0, 20, 0)),
+                200, 400);
+
+        assertEquals(new Rect(0, 0, 30, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_cutoutRight_lessThan_waterfallRight() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                DisplayCutout.fromBoundsAndWaterfall(
+                        new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT},
+                        Insets.of(0, 0, 40, 0)),
+                200, 400);
+
+        assertEquals(new Rect(0, 0, 40, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
     public void computeSafeInsets_bounds() {
-        DisplayCutout cutout = WmDisplayCutout.computeSafeInsets(mCutoutTop, 1000,
-                2000).getDisplayCutout();
+        DisplayCutout cutout =
+                WmDisplayCutout.computeSafeInsets(mCutoutTop, 1000, 2000).getDisplayCutout();
 
         assertEquals(mCutoutTop.getBoundingRects(), cutout.getBoundingRects());
     }
diff --git a/startop/iorap/TEST_MAPPING b/startop/iorap/TEST_MAPPING
index 8c9d4df..1d8119d 100644
--- a/startop/iorap/TEST_MAPPING
+++ b/startop/iorap/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "libiorap-java-tests"
+    },
+    {
+      "name": "iorap-functional-tests"
     }
   ],
   "imports": [
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index c0dfec9..752707e 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -586,6 +586,7 @@
         cv.put(CellBroadcasts.SERIAL_NUMBER, getSerialNumber());
         cv.put(CellBroadcasts.SERVICE_CATEGORY, getServiceCategory());
         cv.put(CellBroadcasts.LANGUAGE_CODE, getLanguageCode());
+        cv.put(CellBroadcasts.DATA_CODING_SCHEME, getDataCodingScheme());
         cv.put(CellBroadcasts.MESSAGE_BODY, getMessageBody());
         cv.put(CellBroadcasts.MESSAGE_FORMAT, getMessageFormat());
         cv.put(CellBroadcasts.MESSAGE_PRIORITY, getMessagePriority());
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index ce2f7c5..82a524b 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -329,6 +329,46 @@
     }
 
     /**
+     * Tests that data in DE (user) apex data directory is restored when apex is rolled back.
+     */
+    @Test
+    public void testRollbackApexDataDirectories_DeUser() throws Exception {
+        pushTestApex();
+
+        // Push files to apex data directory
+        String oldFilePath1 = apexDataDirDeUser(
+                APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
+        String oldFilePath2 =
+                apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
+        assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
+        assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+
+        // Install new version of the APEX with rollback enabled
+        runPhase("testRollbackApexDataDirectories_Phase1");
+        getDevice().reboot();
+
+        // Replace files in data directory
+        getDevice().deleteFile(oldFilePath1);
+        getDevice().deleteFile(oldFilePath2);
+        String newFilePath3 =
+                apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_3;
+        String newFilePath4 =
+                apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4;
+        assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3));
+        assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4));
+
+        // Roll back the APEX
+        runPhase("testRollbackApexDataDirectories_Phase2");
+        getDevice().reboot();
+
+        // Verify that old files have been restored and new files are gone
+        assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
+        assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
+        assertNull(getDevice().pullFile(newFilePath3));
+        assertNull(getDevice().pullFile(newFilePath4));
+    }
+
+    /**
      * Tests that data in CE apex data directory is restored when apex is rolled back.
      */
     @Test
@@ -382,6 +422,10 @@
         return String.format("/data/misc/apexdata/%s", apexName);
     }
 
+    private static String apexDataDirDeUser(String apexName, int userId) {
+        return String.format("/data/misc_de/%d/apexdata/%s", userId, apexName);
+    }
+
     private static String apexDataDirCe(String apexName, int userId) {
         return String.format("/data/misc_ce/%d/apexdata/%s", userId, apexName);
     }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 5592cd7..028c1da 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5741,6 +5741,40 @@
         mCm.unregisterNetworkCallback(defaultCallback);
     }
 
+    @Test
+    public final void testLoseTrusted() throws Exception {
+        final NetworkRequest trustedRequest = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_TRUSTED)
+                .build();
+        final TestNetworkCallback trustedCallback = new TestNetworkCallback();
+        mCm.requestNetwork(trustedRequest, trustedCallback);
+
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        verify(mNetworkManagementService).setDefaultNetId(eq(mCellNetworkAgent.getNetwork().netId));
+
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+        trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+        verify(mNetworkManagementService).setDefaultNetId(eq(mWiFiNetworkAgent.getNetwork().netId));
+
+        mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
+        // There is currently a bug where losing the TRUSTED capability will send a LOST
+        // callback to requests before the available callback, in spite of the semantics
+        // of the requests dictating this should not happen. This is considered benign, but
+        // ideally should be fixed in the future.
+        trustedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        verify(mNetworkManagementService).setDefaultNetId(eq(mCellNetworkAgent.getNetwork().netId));
+
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
+        trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        verify(mNetworkManagementService).clearDefaultNetId();
+
+        mCm.unregisterNetworkCallback(trustedCallback);
+    }
+
     @Ignore // 40%+ flakiness : figure out why and re-enable.
     @Test
     public final void testBatteryStatsNetworkType() throws Exception {
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 954d401..2af118c 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -428,6 +428,12 @@
 
   application_action["meta-data"] = meta_data_action;
 
+  application_action["processes"];
+  application_action["processes"]["deny-permission"];
+  application_action["processes"]["allow-permission"];
+  application_action["processes"]["process"]["deny-permission"];
+  application_action["processes"]["process"]["allow-permission"];
+
   application_action["activity"] = component_action;
   application_action["activity"]["layout"];
 
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index d3958a6..0e32aee 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -30,7 +30,6 @@
         "utils.cpp",
     ],
     cflags: [
-        "-DSTATS_SCHEMA_LEGACY",
         "-Wall",
         "-Werror",
     ],
diff --git a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
index fd23610..462a978 100644
--- a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
+++ b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
@@ -16,6 +16,8 @@
 
 package android.net.wifi;
 
+import android.net.NetworkScore;
+
 /**
  * Interface for Wi-Fi network score callback.
  *
@@ -23,7 +25,7 @@
  */
 oneway interface IScoreChangeCallback
 {
-    void onStatusChange(int sessionId, boolean exiting);
+    void onScoreChange(int sessionId, in NetworkScore score);
 
     void onTriggerUpdateOfWifiUsabilityStats(int sessionId);
 }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 76f9716..5c5471d 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -38,6 +38,7 @@
 import android.net.DhcpInfo;
 import android.net.MacAddress;
 import android.net.Network;
+import android.net.NetworkScore;
 import android.net.NetworkStack;
 import android.net.wifi.hotspot2.IProvisioningCallback;
 import android.net.wifi.hotspot2.OsuProvider;
@@ -256,12 +257,12 @@
      * - {@link #EXTRA_SCAN_AVAILABLE}
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_WIFI_SCAN_AVAILABLE =
-            "android.net.wifi.action.WIFI_SCAN_AVAILABLE";
+    public static final String ACTION_WIFI_SCAN_AVAILABILITY_CHANGED =
+            "android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED";
 
     /**
      * A boolean extra indicating whether scanning is currently available.
-     * Sent in the broadcast {@link #ACTION_WIFI_SCAN_AVAILABLE}.
+     * Sent in the broadcast {@link #ACTION_WIFI_SCAN_AVAILABILITY_CHANGED}.
      * Its value is true if scanning is currently available, false otherwise.
      */
     public static final String EXTRA_SCAN_AVAILABLE = "android.net.wifi.extra.SCAN_AVAILABLE";
@@ -5948,10 +5949,11 @@
          *
          * @param sessionId The ID to indicate current Wi-Fi network connection obtained from
          *                  {@link WifiConnectedNetworkScorer#start(int)}.
-         * @param isUsable The bit to indicate whether current Wi-Fi network is usable or not.
-         *                 Populated by connected network scorer in applications.
+         * @param score The {@link android.net.NetworkScore} object representing the
+         *              characteristics of current Wi-Fi network. Populated by connected network
+         *              scorer in applications.
          */
-        void onStatusChange(int sessionId, boolean isUsable);
+        void onScoreChange(int sessionId, @NonNull NetworkScore score);
 
         /**
          * Called by applications to trigger an update of {@link WifiUsabilityStatsEntry}.
@@ -5977,9 +5979,9 @@
         }
 
         @Override
-        public void onStatusChange(int sessionId, boolean isUsable) {
+        public void onScoreChange(int sessionId, @NonNull NetworkScore score) {
             try {
-                mScoreChangeCallback.onStatusChange(sessionId, isUsable);
+                mScoreChangeCallback.onScoreChange(sessionId, score);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 8250a95..a85f40b 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -837,7 +837,7 @@
      *
      * @param enable set to true to enable scanning, set to false to disable all types of scanning.
      *
-     * @see WifiManager#ACTION_WIFI_SCAN_AVAILABLE
+     * @see WifiManager#ACTION_WIFI_SCAN_AVAILABILITY_CHANGED
      * {@hide}
      */
     @SystemApi