Merge "Change MANAGE_EXTERNAL_STORAGE to signature|appop|preinstalled."
diff --git a/Android.bp b/Android.bp
index bba6185..40d9085 100644
--- a/Android.bp
+++ b/Android.bp
@@ -262,12 +262,13 @@
     name: "framework-updatable-sources",
     srcs: [
         ":framework-appsearch-sources",
-        ":framework-sdkext-sources",
+        ":framework-sdkextensions-sources",
         ":framework-statsd-sources",
         ":framework-tethering-srcs",
         ":updatable-media-srcs",
         ":framework-mediaprovider-sources",
         ":framework-wifi-updatable-sources",
+        ":ike-srcs",
     ]
 }
 
@@ -432,6 +433,7 @@
         // TODO(b/146167933): Use framework-statsd-stubs
         "framework-statsd",
         "framework-wifi-stubs",
+        "ike-stubs",
     ],
     installable: true,
     javac_shard_size: 150,
@@ -481,11 +483,12 @@
         "updatable_media_stubs",
         "framework_mediaprovider_stubs",
         "framework-appsearch", // TODO(b/146218515): should be framework-appsearch-stubs
-        "framework-sdkext-stubs-systemapi",
+        "framework-sdkextensions-stubs-systemapi",
         // TODO(b/146167933): Use framework-statsd-stubs instead.
         "framework-statsd",
         // TODO(b/140299412): should be framework-wifi-stubs
         "framework-wifi",
+        "ike-stubs",
         // TODO(jiyong): add more stubs for APEXes here
     ],
     sdk_version: "core_platform",
@@ -616,6 +619,15 @@
 }
 
 filegroup {
+    name: "framework-ike-shared-srcs",
+    visibility: ["//frameworks/opt/net/ike"],
+    srcs: [
+        "core/java/android/net/annotations/PolicyDirection.java",
+        "telephony/java/android/telephony/Annotation.java",
+    ],
+}
+
+filegroup {
     name: "framework-networkstack-shared-srcs",
     srcs: [
         // TODO: remove these annotations as soon as we can use andoid.support.annotations.*
@@ -648,6 +660,7 @@
         "core/java/com/android/internal/util/Preconditions.java",
         "core/java/com/android/internal/util/State.java",
         "core/java/com/android/internal/util/StateMachine.java",
+        "core/java/com/android/internal/util/TrafficStatsConstants.java",
         "core/java/android/net/shared/Inet4AddressUtils.java",
     ],
 }
@@ -1134,6 +1147,7 @@
     srcs: [
         ":framework-annotations",
         "core/java/android/net/InterfaceConfiguration.java",
+        "core/java/android/os/BasicShellCommandHandler.java",
         "core/java/android/os/HandlerExecutor.java",
         "core/java/android/util/BackupUtils.java",
         "core/java/android/util/LocalLog.java",
diff --git a/apex/blobstore/OWNERS b/apex/blobstore/OWNERS
new file mode 100644
index 0000000..8e04399
--- /dev/null
+++ b/apex/blobstore/OWNERS
@@ -0,0 +1,4 @@
+set noparent
+
+sudheersai@google.com
+yamasani@google.com
diff --git a/apex/blobstore/TEST_MAPPING b/apex/blobstore/TEST_MAPPING
new file mode 100644
index 0000000..4dc0c49
--- /dev/null
+++ b/apex/blobstore/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsBlobStoreTestCases"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/apex/jobscheduler/framework/Android.bp b/apex/jobscheduler/framework/Android.bp
index 98bbe82..ec07426 100644
--- a/apex/jobscheduler/framework/Android.bp
+++ b/apex/jobscheduler/framework/Android.bp
@@ -25,5 +25,6 @@
     },
     libs: [
         "framework-minus-apex",
+        "unsupportedappusage",
     ],
 }
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 8b3b3a2..0bb07ca 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -29,7 +29,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ClipData;
 import android.content.ComponentName;
 import android.net.NetworkRequest;
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index 42cf17b..ef1351e 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -18,8 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
-import android.app.job.IJobCallback;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ClipData;
 import android.net.Network;
 import android.net.Uri;
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java b/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java
index c6631fa..0c45cbf 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java
@@ -19,7 +19,7 @@
 import static android.app.job.JobInfo.NETWORK_BYTES_UNKNOWN;
 
 import android.annotation.BytesLong;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.os.Build;
 import android.os.Parcel;
diff --git a/apex/media/framework/java/android/media/MediaController2.java b/apex/media/framework/java/android/media/MediaController2.java
index c3dd3fe..d059c67 100644
--- a/apex/media/framework/java/android/media/MediaController2.java
+++ b/apex/media/framework/java/android/media/MediaController2.java
@@ -141,6 +141,9 @@
                 // Note: unbindService() throws IllegalArgumentException when it's called twice.
                 return;
             }
+            if (DEBUG) {
+                Log.d(TAG, "closing " + this);
+            }
             mClosed = true;
             if (mServiceConnection != null) {
                 // Note: This should be called even when the bindService() has returned false.
diff --git a/apex/sdkext/TEST_MAPPING b/apex/sdkext/TEST_MAPPING
deleted file mode 100644
index 91947f3..0000000
--- a/apex/sdkext/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "CtsSdkExtTestCases"
-    }
-  ]
-}
diff --git a/apex/sdkext/framework/Android.bp b/apex/sdkext/framework/Android.bp
deleted file mode 100644
index a50dc3d..0000000
--- a/apex/sdkext/framework/Android.bp
+++ /dev/null
@@ -1,71 +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 {
-    default_visibility: [ ":__pkg__" ]
-}
-
-filegroup {
-    name: "framework-sdkext-sources",
-    srcs: [
-        "java/**/*.java",
-    ],
-    path: "java",
-    visibility: [ "//frameworks/base:__pkg__" ] // For the "global" stubs.
-}
-
-java_library {
-    name: "framework-sdkext",
-    srcs: [ ":framework-sdkext-sources" ],
-    sdk_version: "system_current",
-    libs: [ "framework-annotations-lib" ],
-    permitted_packages: [ "android.os.ext" ],
-    installable: true,
-    visibility: [ "//frameworks/base/apex/sdkext:__pkg__" ],
-}
-
-droidstubs {
-    name: "framework-sdkext-droidstubs-publicapi",
-    defaults: [
-        "framework-sdkext-stubs-defaults",
-        "framework-module-stubs-defaults-publicapi",
-    ]
-}
-
-droidstubs {
-    name: "framework-sdkext-droidstubs-systemapi",
-    defaults: [
-        "framework-sdkext-stubs-defaults",
-        "framework-module-stubs-defaults-systemapi",
-    ]
-}
-
-stubs_defaults {
-    name: "framework-sdkext-stubs-defaults",
-    srcs: [
-        ":framework-sdkext-sources",
-        ":framework-annotations",
-    ],
-    sdk_version: "system_current",
-}
-
-java_library {
-    name: "framework-sdkext-stubs-systemapi",
-    srcs: [":framework-sdkext-droidstubs-systemapi"],
-    sdk_version: "system_current",
-    visibility: [
-      "//frameworks/base:__pkg__", // Framework
-      "//frameworks/base/apex/sdkext:__pkg__", // sdkext SDK
-    ]
-}
diff --git a/apex/sdkext/Android.bp b/apex/sdkextensions/Android.bp
similarity index 83%
rename from apex/sdkext/Android.bp
rename to apex/sdkextensions/Android.bp
index f62f167..4c5c2b2 100644
--- a/apex/sdkext/Android.bp
+++ b/apex/sdkextensions/Android.bp
@@ -18,21 +18,26 @@
 
 apex {
     name: "com.android.sdkext",
-    manifest: "manifest.json",
+    defaults: [ "com.android.sdkext-defaults" ],
     binaries: [ "derive_sdk" ],
-    java_libs: [ "framework-sdkext" ],
+    prebuilts: [ "cur_sdkinfo" ],
+    manifest: "manifest.json",
+}
+
+apex_defaults {
+    name: "com.android.sdkext-defaults",
+    java_libs: [ "framework-sdkextensions" ],
     prebuilts: [
-      "com.android.sdkext.ldconfig",
-      "cur_sdkinfo",
-      "derive_sdk.rc",
+        "com.android.sdkext.ldconfig",
+        "derive_sdk.rc",
     ],
     key: "com.android.sdkext.key",
     certificate: ":com.android.sdkext.certificate",
 }
 
 sdk {
-    name: "sdkext-sdk",
-    java_header_libs: [ "framework-sdkext-stubs-systemapi" ],
+    name: "sdkextensions-sdk",
+    java_header_libs: [ "framework-sdkextensions-stubs-systemapi" ],
 }
 
 apex_key {
diff --git a/apex/sdkext/OWNERS b/apex/sdkextensions/OWNERS
similarity index 100%
rename from apex/sdkext/OWNERS
rename to apex/sdkextensions/OWNERS
diff --git a/apex/sdkextensions/TEST_MAPPING b/apex/sdkextensions/TEST_MAPPING
new file mode 100644
index 0000000..7e77623
--- /dev/null
+++ b/apex/sdkextensions/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsSdkExtTestCases"
+    },
+    {
+      "name": "apiextensions_e2e_tests"
+    }
+  ]
+}
diff --git a/apex/sdkext/com.android.sdkext.avbpubkey b/apex/sdkextensions/com.android.sdkext.avbpubkey
similarity index 100%
rename from apex/sdkext/com.android.sdkext.avbpubkey
rename to apex/sdkextensions/com.android.sdkext.avbpubkey
Binary files differ
diff --git a/apex/sdkext/com.android.sdkext.pem b/apex/sdkextensions/com.android.sdkext.pem
similarity index 100%
rename from apex/sdkext/com.android.sdkext.pem
rename to apex/sdkextensions/com.android.sdkext.pem
diff --git a/apex/sdkext/com.android.sdkext.pk8 b/apex/sdkextensions/com.android.sdkext.pk8
similarity index 100%
rename from apex/sdkext/com.android.sdkext.pk8
rename to apex/sdkextensions/com.android.sdkext.pk8
Binary files differ
diff --git a/apex/sdkext/com.android.sdkext.x509.pem b/apex/sdkextensions/com.android.sdkext.x509.pem
similarity index 100%
rename from apex/sdkext/com.android.sdkext.x509.pem
rename to apex/sdkextensions/com.android.sdkext.x509.pem
diff --git a/apex/sdkext/derive_sdk/Android.bp b/apex/sdkextensions/derive_sdk/Android.bp
similarity index 63%
rename from apex/sdkext/derive_sdk/Android.bp
rename to apex/sdkextensions/derive_sdk/Android.bp
index c4e3c29..cf49902 100644
--- a/apex/sdkext/derive_sdk/Android.bp
+++ b/apex/sdkextensions/derive_sdk/Android.bp
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_binary {
-    name: "derive_sdk",
+cc_defaults {
+    name: "derive_sdk-defaults",
     srcs: [
         "derive_sdk.cpp",
         "sdk.proto",
@@ -30,6 +30,24 @@
     ],
 }
 
+cc_binary {
+    name: "derive_sdk",
+    defaults: [ "derive_sdk-defaults" ],
+    apex_available: [ "com.android.sdkext" ],
+    visibility: [ "//frameworks/base/apex/sdkextensions" ]
+}
+
+// Work around testing using a 64-bit test suite on 32-bit test device by
+// using a prefer32 version of derive_sdk in testing.
+cc_binary {
+    name: "derive_sdk_prefer32",
+    defaults: [ "derive_sdk-defaults" ],
+    compile_multilib: "prefer32",
+    stem: "derive_sdk",
+    apex_available: [ "test_com.android.sdkext" ],
+    visibility: [ "//frameworks/base/apex/sdkextensions/testing" ]
+}
+
 prebuilt_etc {
     name: "derive_sdk.rc",
     src: "derive_sdk.rc",
diff --git a/apex/sdkext/derive_sdk/derive_sdk.cpp b/apex/sdkextensions/derive_sdk/derive_sdk.cpp
similarity index 97%
rename from apex/sdkext/derive_sdk/derive_sdk.cpp
rename to apex/sdkextensions/derive_sdk/derive_sdk.cpp
index 0a97116..6fb7ef4 100644
--- a/apex/sdkext/derive_sdk/derive_sdk.cpp
+++ b/apex/sdkextensions/derive_sdk/derive_sdk.cpp
@@ -26,7 +26,7 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 
-#include "frameworks/base/apex/sdkext/derive_sdk/sdk.pb.h"
+#include "frameworks/base/apex/sdkextensions/derive_sdk/sdk.pb.h"
 
 using com::android::sdkext::proto::SdkVersion;
 
diff --git a/apex/sdkext/derive_sdk/derive_sdk.rc b/apex/sdkextensions/derive_sdk/derive_sdk.rc
similarity index 100%
rename from apex/sdkext/derive_sdk/derive_sdk.rc
rename to apex/sdkextensions/derive_sdk/derive_sdk.rc
diff --git a/apex/sdkext/derive_sdk/sdk.proto b/apex/sdkextensions/derive_sdk/sdk.proto
similarity index 100%
rename from apex/sdkext/derive_sdk/sdk.proto
rename to apex/sdkextensions/derive_sdk/sdk.proto
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
new file mode 100644
index 0000000..5504f4e
--- /dev/null
+++ b/apex/sdkextensions/framework/Android.bp
@@ -0,0 +1,74 @@
+// 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 {
+    default_visibility: [ ":__pkg__" ]
+}
+
+filegroup {
+    name: "framework-sdkextensions-sources",
+    srcs: [
+        "java/**/*.java",
+    ],
+    path: "java",
+    visibility: [ "//frameworks/base" ] // For the "global" stubs.
+}
+
+java_library {
+    name: "framework-sdkextensions",
+    srcs: [ ":framework-sdkextensions-sources" ],
+    sdk_version: "system_current",
+    libs: [ "framework-annotations-lib" ],
+    permitted_packages: [ "android.os.ext" ],
+    installable: true,
+    visibility: [
+        "//frameworks/base/apex/sdkextensions",
+        "//frameworks/base/apex/sdkextensions/testing",
+    ],
+}
+
+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",
+    ],
+    sdk_version: "system_current",
+}
+
+java_library {
+    name: "framework-sdkextensions-stubs-systemapi",
+    srcs: [":framework-sdkextensions-droidstubs-systemapi"],
+    sdk_version: "system_current",
+    visibility: [
+        "//frameworks/base", // Framework
+        "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
+    ]
+}
diff --git a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
similarity index 100%
rename from apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
rename to apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
diff --git a/apex/sdkext/framework/java/android/os/ext/package.html b/apex/sdkextensions/framework/java/android/os/ext/package.html
similarity index 100%
rename from apex/sdkext/framework/java/android/os/ext/package.html
rename to apex/sdkextensions/framework/java/android/os/ext/package.html
diff --git a/apex/sdkext/gen_sdkinfo.py b/apex/sdkextensions/gen_sdkinfo.py
similarity index 100%
rename from apex/sdkext/gen_sdkinfo.py
rename to apex/sdkextensions/gen_sdkinfo.py
diff --git a/apex/sdkext/ld.config.txt b/apex/sdkextensions/ld.config.txt
similarity index 91%
rename from apex/sdkext/ld.config.txt
rename to apex/sdkextensions/ld.config.txt
index b447068..dcc69b8 100644
--- a/apex/sdkext/ld.config.txt
+++ b/apex/sdkextensions/ld.config.txt
@@ -1,10 +1,10 @@
 # Copyright (C) 2019 The Android Open Source Project
 #
-# Bionic loader config file for the sdkext apex.
+# Bionic loader config file for the sdkextensions apex.
 
-dir.sdkext = /apex/com.android.sdkext/bin/
+dir.sdkextensions = /apex/com.android.sdkext/bin/
 
-[sdkext]
+[sdkextensions]
 additional.namespaces = platform
 
 namespace.default.isolated = true
diff --git a/apex/sdkext/manifest.json b/apex/sdkextensions/manifest.json
similarity index 100%
rename from apex/sdkext/manifest.json
rename to apex/sdkextensions/manifest.json
diff --git a/apex/sdkext/sdk.proto b/apex/sdkextensions/sdk.proto
similarity index 100%
rename from apex/sdkext/sdk.proto
rename to apex/sdkextensions/sdk.proto
diff --git a/apex/sdkextensions/testing/Android.bp b/apex/sdkextensions/testing/Android.bp
new file mode 100644
index 0000000..e6451cc
--- /dev/null
+++ b/apex/sdkextensions/testing/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+apex {
+    name: "test_com.android.sdkext",
+    visibility: [ "//system/apex/tests" ],
+    defaults: ["com.android.sdkext-defaults"],
+    manifest: "test_manifest.json",
+    prebuilts: [ "sdkinfo_45" ],
+    file_contexts: ":com.android.sdkext-file_contexts",
+    installable: false, // Should never be installed on the systemimage
+    multilib: {
+        prefer32: {
+            binaries: ["derive_sdk_prefer32"],
+        },
+    },
+    // The automated test infra ends up building this apex for 64+32-bit and
+    // then installs it on a 32-bit-only device. Work around this weirdness
+    // by preferring 32-bit.
+    compile_multilib: "prefer32",
+}
+
+genrule {
+    name: "sdkinfo_45_src",
+    out: [ "sdkinfo.binarypb" ],
+    tools: [ "gen_sdkinfo" ],
+    cmd: "$(location) -v 45 -o $(out)",
+}
+
+prebuilt_etc {
+    name: "sdkinfo_45",
+    src: ":sdkinfo_45_src",
+    filename: "sdkinfo.binarypb",
+    installable: false,
+}
diff --git a/apex/sdkextensions/testing/test_manifest.json b/apex/sdkextensions/testing/test_manifest.json
new file mode 100644
index 0000000..1b4a2b0
--- /dev/null
+++ b/apex/sdkextensions/testing/test_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.sdkext",
+  "version": 2147483647
+}
diff --git a/apex/statsd/aidl/android/os/IStatsManagerService.aidl b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
index 05583677..dec5634 100644
--- a/apex/statsd/aidl/android/os/IStatsManagerService.aidl
+++ b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
@@ -85,4 +85,44 @@
      * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
      */
     void unsetBroadcastSubscriber(long configKey, long subscriberId, in String packageName);
+
+    /**
+     * Returns the most recently registered experiment IDs.
+     *
+     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+     */
+    long[] getRegisteredExperimentIds();
+
+    /**
+     * Fetches metadata across statsd. Returns byte array representing wire-encoded proto.
+     *
+     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+     */
+    byte[] getMetadata(in String packageName);
+
+    /**
+     * Fetches data for the specified configuration key. Returns a byte array representing proto
+     * wire-encoded of ConfigMetricsReportList.
+     *
+     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+     */
+    byte[] getData(in long key, in String packageName);
+
+    /**
+     * Sets a configuration with the specified config id and subscribes to updates for this
+     * configuration id. Broadcasts will be sent if this configuration needs to be collected.
+     * The configuration must be a wire-encoded StatsdConfig. The receiver for this data is
+     * registered in a separate function.
+     *
+     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+     */
+    void addConfiguration(in long configId, in byte[] config, in String packageName);
+
+    /**
+     * Removes the configuration with the matching config id. No-op if this config id does not
+     * exist.
+     *
+     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+     */
+    void removeConfiguration(in long configId, in String packageName);
 }
\ No newline at end of file
diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl
index 9358439..c409f51 100644
--- a/apex/statsd/aidl/android/os/IStatsd.aidl
+++ b/apex/statsd/aidl/android/os/IStatsd.aidl
@@ -89,24 +89,24 @@
      *
      * Requires Manifest.permission.DUMP.
      */
-    byte[] getData(in long key, in String packageName);
+    byte[] getData(in long key, int callingUid);
 
     /**
      * Fetches metadata across statsd. Returns byte array representing wire-encoded proto.
      *
      * Requires Manifest.permission.DUMP.
      */
-    byte[] getMetadata(in String packageName);
+    byte[] getMetadata();
 
     /**
-     * Sets a configuration with the specified config key and subscribes to updates for this
+     * Sets a configuration with the specified config id and subscribes to updates for this
      * configuration key. Broadcasts will be sent if this configuration needs to be collected.
      * The configuration must be a wire-encoded StatsdConfig. The receiver for this data is
      * registered in a separate function.
      *
      * Requires Manifest.permission.DUMP.
      */
-    void addConfiguration(in long configKey, in byte[] config, in String packageName);
+    void addConfiguration(in long configId, in byte[] config, in int callingUid);
 
     /**
      * Registers the given pending intent for this config key. This intent is invoked when the
@@ -143,12 +143,12 @@
     void removeActiveConfigsChangedOperation(int callingUid);
 
     /**
-     * Removes the configuration with the matching config key. No-op if this config key does not
+     * Removes the configuration with the matching config id. No-op if this config id does not
      * exist.
      *
      * Requires Manifest.permission.DUMP.
      */
-    void removeConfiguration(in long configKey, in String packageName);
+    void removeConfiguration(in long configId, in int callingUid);
 
     /**
      * Set the PendingIntentRef to be used when broadcasting subscriber
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
index 1d03e3b..b27d0f7 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -19,6 +19,7 @@
 import static com.android.server.stats.StatsCompanion.PendingIntentRef;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -245,20 +246,127 @@
         }
     }
 
+    @Override
+    public long[] getRegisteredExperimentIds() throws IllegalStateException {
+        enforceDumpAndUsageStatsPermission(null);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            IStatsd statsd = waitForStatsd();
+            if (statsd != null) {
+                return statsd.getRegisteredExperimentIds();
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to getRegisteredExperimentIds with statsd");
+            throw new IllegalStateException(e.getMessage(), e);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        throw new IllegalStateException("Failed to connect to statsd to registerExperimentIds");
+    }
+
+    @Override
+    public byte[] getMetadata(String packageName) throws IllegalStateException {
+        enforceDumpAndUsageStatsPermission(packageName);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            IStatsd statsd = waitForStatsd();
+            if (statsd != null) {
+                return statsd.getMetadata();
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to getMetadata with statsd");
+            throw new IllegalStateException(e.getMessage(), e);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        throw new IllegalStateException("Failed to connect to statsd to getMetadata");
+    }
+
+    @Override
+    public byte[] getData(long key, String packageName) throws IllegalStateException {
+        enforceDumpAndUsageStatsPermission(packageName);
+        int callingUid = Binder.getCallingUid();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            IStatsd statsd = waitForStatsd();
+            if (statsd != null) {
+                return statsd.getData(key, callingUid);
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to getData with statsd");
+            throw new IllegalStateException(e.getMessage(), e);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        throw new IllegalStateException("Failed to connect to statsd to getData");
+    }
+
+    @Override
+    public void addConfiguration(long configId, byte[] config, String packageName)
+            throws IllegalStateException {
+        enforceDumpAndUsageStatsPermission(packageName);
+        int callingUid = Binder.getCallingUid();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            IStatsd statsd = waitForStatsd();
+            if (statsd != null) {
+                statsd.addConfiguration(configId, config, callingUid);
+                return;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to addConfiguration with statsd");
+            throw new IllegalStateException(e.getMessage(), e);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        throw new IllegalStateException("Failed to connect to statsd to addConfig");
+    }
+
+    @Override
+    public void removeConfiguration(long configId, String packageName)
+            throws IllegalStateException {
+        enforceDumpAndUsageStatsPermission(packageName);
+        int callingUid = Binder.getCallingUid();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            IStatsd statsd = waitForStatsd();
+            if (statsd != null) {
+                statsd.removeConfiguration(configId, callingUid);
+                return;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to removeConfiguration with statsd");
+            throw new IllegalStateException(e.getMessage(), e);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        throw new IllegalStateException("Failed to connect to statsd to removeConfig");
+    }
+
     void setStatsCompanionService(StatsCompanionService statsCompanionService) {
         mStatsCompanionService = statsCompanionService;
     }
 
-    private void enforceDumpAndUsageStatsPermission(String packageName) {
+    /**
+     * Checks that the caller has both DUMP and PACKAGE_USAGE_STATS permissions. Also checks that
+     * the caller has USAGE_STATS_PERMISSION_OPS for the specified packageName if it is not null.
+     *
+     * @param packageName The packageName to check USAGE_STATS_PERMISSION_OPS.
+     */
+    private void enforceDumpAndUsageStatsPermission(@Nullable String packageName) {
         int callingUid = Binder.getCallingUid();
         int callingPid = Binder.getCallingPid();
 
         if (callingPid == Process.myPid()) {
             return;
         }
+
         mContext.enforceCallingPermission(Manifest.permission.DUMP, null);
         mContext.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS, null);
 
+        if (packageName == null) {
+            return;
+        }
         AppOpsManager appOpsManager = (AppOpsManager) mContext
                 .getSystemService(Context.APP_OPS_SERVICE);
         switch (appOpsManager.noteOp(USAGE_STATS_PERMISSION_OPS,
diff --git a/api/current.txt b/api/current.txt
index 51641f2..6c13dbf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9504,6 +9504,7 @@
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
     method @Nullable public final String getCallingFeatureId();
     method @Nullable public final String getCallingPackage();
+    method @Nullable public final String getCallingPackageUnchecked();
     method @Nullable public final android.content.Context getContext();
     method @Nullable public final android.content.pm.PathPermission[] getPathPermissions();
     method @Nullable public final String getReadPermission();
@@ -9513,6 +9514,7 @@
     method @Nullable public abstract android.net.Uri insert(@NonNull android.net.Uri, @Nullable android.content.ContentValues);
     method @Nullable public android.net.Uri insert(@NonNull android.net.Uri, @Nullable android.content.ContentValues, @Nullable android.os.Bundle);
     method protected boolean isTemporary();
+    method public void onCallingPackageChanged();
     method public void onConfigurationChanged(android.content.res.Configuration);
     method public abstract boolean onCreate();
     method public void onLowMemory();
@@ -9627,13 +9629,13 @@
     ctor public ContentProviderResult(@NonNull android.net.Uri);
     ctor public ContentProviderResult(int);
     ctor public ContentProviderResult(@NonNull android.os.Bundle);
-    ctor public ContentProviderResult(@NonNull Exception);
+    ctor public ContentProviderResult(@NonNull Throwable);
     ctor public ContentProviderResult(android.os.Parcel);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.ContentProviderResult> CREATOR;
     field @Nullable public final Integer count;
-    field @Nullable public final Exception exception;
+    field @Nullable public final Throwable exception;
     field @Nullable public final android.os.Bundle extras;
     field @Nullable public final android.net.Uri uri;
   }
@@ -9998,6 +10000,7 @@
     field public static final String MEDIA_ROUTER_SERVICE = "media_router";
     field public static final String MEDIA_SESSION_SERVICE = "media_session";
     field public static final String MIDI_SERVICE = "midi";
+    field public static final String MMS_SERVICE = "mms";
     field public static final int MODE_APPEND = 32768; // 0x8000
     field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
     field @Deprecated public static final int MODE_MULTI_PROCESS = 4; // 0x4
@@ -11925,6 +11928,7 @@
     field public static final String FEATURE_STRONGBOX_KEYSTORE = "android.hardware.strongbox_keystore";
     field public static final String FEATURE_TELEPHONY = "android.hardware.telephony";
     field public static final String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
+    field public static final String FEATURE_TELEPHONY_DATA = "android.hardware.telephony.data";
     field public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
     field public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
     field public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
@@ -13323,6 +13327,7 @@
     method @Deprecated public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String[], String, String);
     method public int delete(@NonNull android.database.sqlite.SQLiteDatabase, @Nullable String, @Nullable String[]);
     method @Nullable public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
+    method @Nullable public java.util.Collection<java.util.regex.Pattern> getProjectionGreylist();
     method @Nullable public java.util.Map<java.lang.String,java.lang.String> getProjectionMap();
     method @Nullable public String getTables();
     method public long insert(@NonNull android.database.sqlite.SQLiteDatabase, @NonNull android.content.ContentValues);
@@ -13335,6 +13340,7 @@
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String, android.os.CancellationSignal);
     method public void setCursorFactory(@Nullable android.database.sqlite.SQLiteDatabase.CursorFactory);
     method public void setDistinct(boolean);
+    method public void setProjectionGreylist(@Nullable java.util.Collection<java.util.regex.Pattern>);
     method public void setProjectionMap(@Nullable java.util.Map<java.lang.String,java.lang.String>);
     method public void setStrict(boolean);
     method public void setStrictColumns(boolean);
@@ -25781,8 +25787,8 @@
     method @Nullable public android.graphics.Bitmap getImageAtIndex(int);
     method @Nullable public android.graphics.Bitmap getPrimaryImage(@NonNull android.media.MediaMetadataRetriever.BitmapParams);
     method @Nullable public android.graphics.Bitmap getPrimaryImage();
-    method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
-    method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
+    method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, @IntRange(from=1) int, @IntRange(from=1) int);
+    method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, @IntRange(from=1) int, @IntRange(from=1) int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
     method public void release();
     method public void setDataSource(String) throws java.lang.IllegalArgumentException;
     method public void setDataSource(String, java.util.Map<java.lang.String,java.lang.String>) throws java.lang.IllegalArgumentException;
@@ -28683,6 +28689,8 @@
     method public int getVideoWidth();
     method public boolean isAudioDescription();
     method public boolean isEncrypted();
+    method public boolean isHardOfHearing();
+    method public boolean isSpokenSubtitle();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TvTrackInfo> CREATOR;
     field public static final int TYPE_AUDIO = 0; // 0x0
@@ -28699,7 +28707,9 @@
     method public android.media.tv.TvTrackInfo.Builder setDescription(CharSequence);
     method @NonNull public android.media.tv.TvTrackInfo.Builder setEncrypted(boolean);
     method public android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
+    method @NonNull public android.media.tv.TvTrackInfo.Builder setHardOfHearing(boolean);
     method public android.media.tv.TvTrackInfo.Builder setLanguage(String);
+    method @NonNull public android.media.tv.TvTrackInfo.Builder setSpokenSubtitle(boolean);
     method public android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
     method public android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
     method public android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
@@ -29011,6 +29021,37 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortal> CREATOR;
   }
 
+  public class ConnectivityDiagnosticsManager {
+    method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
+    method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
+    field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
+    field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
+  }
+
+  public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
+    ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback();
+    method public void onConnectivityReport(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
+    method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport);
+    method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
+  }
+
+  public static class ConnectivityDiagnosticsManager.ConnectivityReport {
+    ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
+    field @NonNull public final android.os.PersistableBundle additionalInfo;
+    field @NonNull public final android.net.LinkProperties linkProperties;
+    field @NonNull public final android.net.Network network;
+    field @NonNull public final android.net.NetworkCapabilities networkCapabilities;
+    field public final long reportTimestamp;
+  }
+
+  public static class ConnectivityDiagnosticsManager.DataStallReport {
+    ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.os.PersistableBundle);
+    field public final int detectionMethod;
+    field @NonNull public final android.net.Network network;
+    field public final long reportTimestamp;
+    field @NonNull public final android.os.PersistableBundle stallDetails;
+  }
+
   public class ConnectivityManager {
     method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
     method public boolean bindProcessToNetwork(@Nullable android.net.Network);
@@ -30280,6 +30321,7 @@
   @Deprecated public static class WifiConfiguration.AuthAlgorithm {
     field @Deprecated public static final int LEAP = 2; // 0x2
     field @Deprecated public static final int OPEN = 0; // 0x0
+    field @Deprecated public static final int SAE = 3; // 0x3
     field @Deprecated public static final int SHARED = 1; // 0x1
     field @Deprecated public static final String[] strings;
     field @Deprecated public static final String varName = "auth_alg";
@@ -35682,7 +35724,9 @@
     method public int describeContents();
     method @Nullable public android.os.PersistableBundle getPersistableBundle(@Nullable String);
     method public void putPersistableBundle(@Nullable String, @Nullable android.os.PersistableBundle);
+    method @NonNull public static android.os.PersistableBundle readFromStream(@NonNull java.io.InputStream) throws java.io.IOException;
     method public void writeToParcel(android.os.Parcel, int);
+    method public void writeToStream(@NonNull java.io.OutputStream) throws java.io.IOException;
     field @NonNull public static final android.os.Parcelable.Creator<android.os.PersistableBundle> CREATOR;
     field public static final android.os.PersistableBundle EMPTY;
   }
@@ -36350,15 +36394,22 @@
     method public boolean isObbMounted(String);
     method public boolean mountObb(String, String, android.os.storage.OnObbStateChangeListener);
     method @NonNull public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback, android.os.Handler) throws java.io.IOException;
+    method public void registerStorageVolumeCallback(@NonNull java.util.concurrent.Executor, @NonNull android.os.storage.StorageManager.StorageVolumeCallback);
     method public void setCacheBehaviorGroup(java.io.File, boolean) throws java.io.IOException;
     method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
     method public boolean unmountObb(String, boolean, android.os.storage.OnObbStateChangeListener);
+    method public void unregisterStorageVolumeCallback(@NonNull android.os.storage.StorageManager.StorageVolumeCallback);
     field public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
     field public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
     field public static final String EXTRA_UUID = "android.os.storage.extra.UUID";
     field public static final java.util.UUID UUID_DEFAULT;
   }
 
+  public static class StorageManager.StorageVolumeCallback {
+    ctor public StorageManager.StorageVolumeCallback();
+    method public void onStateChanged(@NonNull android.os.storage.StorageVolume);
+  }
+
   public final class StorageVolume implements android.os.Parcelable {
     method @Deprecated @Nullable public android.content.Intent createAccessIntent(String);
     method @NonNull public android.content.Intent createOpenDocumentTreeIntent();
@@ -45157,6 +45208,7 @@
 
   public static final class CarrierConfigManager.Ims {
     field public static final String KEY_PREFIX = "ims.";
+    field public static final String KEY_WIFI_OFF_DEFERRING_TIME_INT = "ims.wifi_off_deferring_time_int";
   }
 
   public abstract class CellIdentity implements android.os.Parcelable {
@@ -45465,6 +45517,11 @@
     method @Nullable public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, @NonNull java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
   }
 
+  public final class MmsManager {
+    method public void downloadMultimediaMessage(int, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent);
+    method public void sendMultimediaMessage(int, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent);
+  }
+
   @Deprecated public class NeighboringCellInfo implements android.os.Parcelable {
     ctor @Deprecated public NeighboringCellInfo();
     ctor @Deprecated public NeighboringCellInfo(int, int);
@@ -45661,6 +45718,7 @@
     method public String getOperatorNumeric();
     method public boolean getRoaming();
     method public int getState();
+    method public boolean isSearching();
     method public void setIsManualSelection(boolean);
     method public void setOperatorName(String, String, String);
     method public void setRoaming(boolean);
@@ -45701,8 +45759,8 @@
     method public String createAppSpecificSmsToken(android.app.PendingIntent);
     method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent);
     method public java.util.ArrayList<java.lang.String> divideMessage(String);
-    method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
-    method @Nullable public android.os.Bundle getCarrierConfigValues();
+    method @Deprecated public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
+    method @NonNull public android.os.Bundle getCarrierConfigValues();
     method public static android.telephony.SmsManager getDefault();
     method public static int getDefaultSmsSubscriptionId();
     method public static android.telephony.SmsManager getSmsManagerForSubscriptionId(int);
@@ -45711,7 +45769,7 @@
     method public int getSubscriptionId();
     method public void injectSmsPdu(byte[], String, android.app.PendingIntent);
     method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
-    method public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent);
+    method @Deprecated public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent);
     method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
     method public void sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent);
     method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.SEND_SMS}) public void sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent);
@@ -45919,6 +45977,7 @@
   public class SubscriptionManager {
     method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
     method public void addOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
+    method public void addOnSubscriptionsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void addSubscriptionsIntoGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
     method public boolean canManageSubscription(android.telephony.SubscriptionInfo);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>);
@@ -46077,12 +46136,12 @@
     method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
     method public boolean hasCarrierPrivileges();
     method public boolean hasIccCard();
-    method public boolean iccCloseLogicalChannel(int);
-    method public byte[] iccExchangeSimIO(int, int, int, int, int, String);
+    method @Deprecated public boolean iccCloseLogicalChannel(int);
+    method @Deprecated public byte[] iccExchangeSimIO(int, int, int, int, int, String);
     method @Deprecated public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(String);
-    method public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(String, int);
-    method public String iccTransmitApduBasicChannel(int, int, int, int, int, String);
-    method public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
+    method @Deprecated public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(String, int);
+    method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String);
+    method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
     method public boolean isConcurrentVoiceAndDataSupported();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
@@ -46100,7 +46159,7 @@
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
     method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
     method public void sendDialerSpecialCode(String);
-    method public String sendEnvelopeWithStatus(String);
+    method @Deprecated public String sendEnvelopeWithStatus(String);
     method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
     method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 41ded16..4ab8d58 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -567,6 +567,7 @@
   }
 
   public class DownloadManager {
+    method @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) public void onMediaStoreDownloadsDeleted(@NonNull android.util.LongSparseArray<java.lang.String>);
     field public static final String ACTION_DOWNLOAD_COMPLETED = "android.intent.action.DOWNLOAD_COMPLETED";
   }
 
@@ -677,6 +678,7 @@
   public class StatusBarManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
+    method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
   }
 
   public static final class StatusBarManager.DisableInfo {
@@ -799,6 +801,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedKiosk();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isOrganizationOwnedDeviceWithManagedProfile();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUnattendedManagedKiosk();
     method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
     method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
@@ -1652,11 +1655,17 @@
     method @NonNull public final android.os.UserHandle getSendingUser();
   }
 
+  public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
+    method public int checkUriPermission(@NonNull android.net.Uri, int, int);
+  }
+
   public class ContentProviderClient implements java.lang.AutoCloseable {
     method @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(long);
   }
 
   public abstract class ContentResolver {
+    method @NonNull public static android.net.Uri decodeFromFile(@NonNull java.io.File);
+    method @NonNull public static java.io.File encodeToFile(@NonNull android.net.Uri);
     method @Nullable @RequiresPermission("android.permission.CACHE_CONTENT") public android.os.Bundle getCache(@NonNull android.net.Uri);
     method @RequiresPermission("android.permission.CACHE_CONTENT") public void putCache(@NonNull android.net.Uri, @Nullable android.os.Bundle);
   }
@@ -1697,6 +1706,7 @@
     field public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry";
     field public static final String TETHERING_SERVICE = "tethering";
     field public static final String VR_SERVICE = "vrmanager";
+    field public static final String WIFI_COND_SERVICE = "wificond";
     field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
     field public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
   }
@@ -3578,9 +3588,17 @@
   }
 
   public class UsbManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public long getCurrentFunctions();
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USB) public java.util.List<android.hardware.usb.UsbPort> getPorts();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
     field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
+    field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
+    field public static final long FUNCTION_NONE = 0L; // 0x0L
+    field public static final long FUNCTION_RNDIS = 32L; // 0x20L
+    field public static final String USB_CONFIGURED = "configured";
+    field public static final String USB_CONNECTED = "connected";
+    field public static final String USB_FUNCTION_RNDIS = "rndis";
   }
 
   public final class UsbPort {
@@ -4677,6 +4695,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
+    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
@@ -4684,6 +4703,7 @@
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
     field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
     field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
@@ -4854,6 +4874,17 @@
     field public final android.net.WifiKey wifiKey;
   }
 
+  public class NetworkProvider {
+    ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest);
+    method @Nullable public android.os.Messenger getMessenger();
+    method @NonNull public String getName();
+    method public int getProviderId();
+    method public void onNetworkRequested(@NonNull android.net.NetworkRequest, int, int);
+    method public void onRequestWithdrawn(@NonNull android.net.NetworkRequest);
+    field public static final int ID_NONE = -1; // 0xffffffff
+  }
+
   public abstract class NetworkRecommendationProvider {
     ctor public NetworkRecommendationProvider(android.content.Context, java.util.concurrent.Executor);
     method public final android.os.IBinder getBinder();
@@ -4897,6 +4928,33 @@
     field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
   }
 
+  public final class NetworkStats implements android.os.Parcelable {
+    ctor public NetworkStats(long, int);
+    method @NonNull public android.net.NetworkStats add(@NonNull android.net.NetworkStats);
+    method @NonNull public android.net.NetworkStats addValues(@NonNull android.net.NetworkStats.Entry);
+    method public int describeContents();
+    method @NonNull public android.net.NetworkStats subtract(@NonNull android.net.NetworkStats);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStats> CREATOR;
+    field public static final int DEFAULT_NETWORK_NO = 0; // 0x0
+    field public static final int DEFAULT_NETWORK_YES = 1; // 0x1
+    field @Nullable public static final String IFACE_ALL;
+    field public static final String IFACE_VT = "vt_data0";
+    field public static final int METERED_NO = 0; // 0x0
+    field public static final int METERED_YES = 1; // 0x1
+    field public static final int ROAMING_NO = 0; // 0x0
+    field public static final int ROAMING_YES = 1; // 0x1
+    field public static final int SET_DEFAULT = 0; // 0x0
+    field public static final int SET_FOREGROUND = 1; // 0x1
+    field public static final int TAG_NONE = 0; // 0x0
+    field public static final int UID_ALL = -1; // 0xffffffff
+    field public static final int UID_TETHERING = -5; // 0xfffffffb
+  }
+
+  public static class NetworkStats.Entry {
+    ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long);
+  }
+
   public final class RouteInfo implements android.os.Parcelable {
     ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int);
     method public int getType();
@@ -5488,6 +5546,25 @@
 
 }
 
+package android.net.netstats.provider {
+
+  public abstract class AbstractNetworkStatsProvider {
+    ctor public AbstractNetworkStatsProvider();
+    method public abstract void requestStatsUpdate(int);
+    method public abstract void setAlert(long);
+    method public abstract void setLimit(@NonNull String, long);
+    field public static final int QUOTA_UNLIMITED = -1; // 0xffffffff
+  }
+
+  public class NetworkStatsProviderCallback {
+    method public void onAlertReached();
+    method public void onLimitReached();
+    method public void onStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats);
+    method public void unregister();
+  }
+
+}
+
 package android.net.util {
 
   public final class SocketUtils {
@@ -5927,6 +6004,7 @@
   public class WifiManager {
     method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinPasspoint(@NonNull String, boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
@@ -6003,6 +6081,7 @@
     field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK";
     field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
     field public static final String EXTRA_URL = "android.net.wifi.extra.URL";
+    field public static final String EXTRA_WIFI_AP_FAILURE_REASON = "android.net.wifi.extra.WIFI_AP_FAILURE_REASON";
     field public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME";
     field public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE";
     field public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
@@ -6291,6 +6370,10 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.OsuProvider> CREATOR;
   }
 
+  public final class PasspointConfiguration implements android.os.Parcelable {
+    method public boolean isAutoJoinEnabled();
+  }
+
   public abstract class ProvisioningCallback {
     ctor public ProvisioningCallback();
     method public abstract void onProvisioningComplete();
@@ -6957,6 +7040,10 @@
     method public boolean hasSingleFileDescriptor();
   }
 
+  public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable {
+    method @NonNull public static android.os.ParcelFileDescriptor wrap(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.Handler, @NonNull android.os.ParcelFileDescriptor.OnCloseListener) throws java.io.IOException;
+  }
+
   public final class PowerManager {
     method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long);
     method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend();
@@ -7065,7 +7152,22 @@
   }
 
   public class TelephonyServiceManager {
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getCarrierConfigServiceRegisterer();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getEuiccCardControllerServiceRegisterer();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getEuiccControllerService();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getIccPhoneBookServiceRegisterer();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getNetworkPolicyServiceRegisterer();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getOpportunisticNetworkServiceRegisterer();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPackageManagerServiceRegisterer();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPermissionManagerServiceRegisterer();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPhoneSubServiceRegisterer();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getSmsServiceRegisterer();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getSubscriptionServiceRegisterer();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyImsServiceRegisterer();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRcsMessageServiceRegisterer();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRegistryServiceRegisterer();
     method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyServiceRegisterer();
+    method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getWindowServiceRegisterer();
   }
 
   public static class TelephonyServiceManager.ServiceNotFoundException extends java.lang.Exception {
@@ -7081,11 +7183,13 @@
 
   public class UpdateEngine {
     ctor public UpdateEngine();
+    method @NonNull public android.os.UpdateEngine.AllocateSpaceResult allocateSpace(@NonNull String, @NonNull String[]);
     method public void applyPayload(String, long, long, String[]);
     method public void applyPayload(@NonNull android.os.ParcelFileDescriptor, long, long, @NonNull String[]);
     method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler);
     method public boolean bind(android.os.UpdateEngineCallback);
     method public void cancel();
+    method public int cleanupAppliedPayload();
     method public void resetStatus();
     method public void resume();
     method public void suspend();
@@ -7093,14 +7197,21 @@
     method public boolean verifyPayloadMetadata(String);
   }
 
+  public static final class UpdateEngine.AllocateSpaceResult {
+    method public int errorCode();
+    method public long freeSpaceRequired();
+  }
+
   public static final class UpdateEngine.ErrorCodeConstants {
     ctor public UpdateEngine.ErrorCodeConstants();
+    field public static final int DEVICE_CORRUPTED = 61; // 0x3d
     field public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12; // 0xc
     field public static final int DOWNLOAD_TRANSFER_ERROR = 9; // 0x9
     field public static final int ERROR = 1; // 0x1
     field public static final int FILESYSTEM_COPIER_ERROR = 4; // 0x4
     field public static final int INSTALL_DEVICE_OPEN_ERROR = 7; // 0x7
     field public static final int KERNEL_DEVICE_OPEN_ERROR = 8; // 0x8
+    field public static final int NOT_ENOUGH_SPACE = 60; // 0x3c
     field public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; // 0xa
     field public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6; // 0x6
     field public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; // 0xb
@@ -7336,6 +7447,10 @@
     field @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1
   }
 
+  public final class StorageVolume implements android.os.Parcelable {
+    method @NonNull public String getId();
+  }
+
 }
 
 package android.permission {
@@ -7755,6 +7870,7 @@
     field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_DETAIL_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_DETAIL_SETTINGS";
     field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
     field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
+    field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI";
   }
 
   public static final class Settings.Global extends android.provider.Settings.NameValueTable {
@@ -7774,6 +7890,7 @@
     field public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
     field public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
     field public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
+    field public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
     field public static final String TETHER_SUPPORTED = "tether_supported";
     field public static final String THEATER_MODE_ON = "theater_mode_on";
     field public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
@@ -7913,7 +8030,71 @@
   }
 
   public static final class Telephony.SimInfo {
+    field public static final String ACCESS_RULES = "access_rules";
+    field public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS = "access_rules_from_carrier_configs";
+    field public static final String CARD_ID = "card_id";
+    field public static final String CARRIER_ID = "carrier_id";
+    field public static final String CARRIER_NAME = "carrier_name";
+    field public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
+    field public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration";
+    field public static final String CB_ALERT_SPEECH = "enable_alert_speech";
+    field public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate";
+    field public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts";
+    field public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
+    field public static final String CB_CMAS_TEST_ALERT = "enable_cmas_test_alerts";
+    field public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts";
+    field public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
+    field public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts";
+    field public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
+    field public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts";
+    field public static final String COLOR = "color";
     field @NonNull public static final android.net.Uri CONTENT_URI;
+    field public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules";
+    field public static final String DATA_ROAMING = "data_roaming";
+    field public static final int DATA_ROAMING_DEFAULT = 0; // 0x0
+    field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
+    field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
+    field public static final String DISPLAY_NAME = "display_name";
+    field public static final String EHPLMNS = "ehplmns";
+    field public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+    field public static final String GROUP_OWNER = "group_owner";
+    field public static final String GROUP_UUID = "group_uuid";
+    field public static final String HPLMNS = "hplmns";
+    field public static final String ICC_ID = "icc_id";
+    field public static final String IMSI = "imsi";
+    field public static final String ISO_COUNTRY_CODE = "iso_country_code";
+    field public static final String IS_EMBEDDED = "is_embedded";
+    field public static final String IS_OPPORTUNISTIC = "is_opportunistic";
+    field public static final String IS_REMOVABLE = "is_removable";
+    field public static final String MCC = "mcc";
+    field public static final String MCC_STRING = "mcc_string";
+    field public static final String MNC = "mnc";
+    field public static final String MNC_STRING = "mnc_string";
+    field public static final String NAME_SOURCE = "name_source";
+    field public static final int NAME_SOURCE_CARRIER = 3; // 0x3
+    field public static final int NAME_SOURCE_DEFAULT = 0; // 0x0
+    field public static final int NAME_SOURCE_SIM_PNN = 4; // 0x4
+    field public static final int NAME_SOURCE_SIM_SPN = 1; // 0x1
+    field public static final int NAME_SOURCE_USER_INPUT = 2; // 0x2
+    field public static final String NUMBER = "number";
+    field public static final String PROFILE_CLASS = "profile_class";
+    field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
+    field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2
+    field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1
+    field public static final int PROFILE_CLASS_TESTING = 0; // 0x0
+    field public static final int PROFILE_CLASS_UNSET = -1; // 0xffffffff
+    field public static final int SIM_NOT_INSERTED = -1; // 0xffffffff
+    field public static final String SIM_SLOT_INDEX = "sim_id";
+    field public static final String SUBSCRIPTION_TYPE = "subscription_type";
+    field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
+    field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
+    field public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled";
+    field public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
+    field public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+    field public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+    field public static final String WFC_IMS_MODE = "wfc_ims_mode";
+    field public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+    field public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
   }
 
   public static final class Telephony.Sms.Intents {
@@ -9180,30 +9361,37 @@
 
   public abstract class CellIdentity implements android.os.Parcelable {
     method @NonNull public abstract android.telephony.CellLocation asCellLocation();
+    method @NonNull public abstract android.telephony.CellIdentity sanitizeLocationInfo();
   }
 
   public final class CellIdentityCdma extends android.telephony.CellIdentity {
     method @NonNull public android.telephony.cdma.CdmaCellLocation asCellLocation();
+    method @NonNull public android.telephony.CellIdentityCdma sanitizeLocationInfo();
   }
 
   public final class CellIdentityGsm extends android.telephony.CellIdentity {
     method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
+    method @NonNull public android.telephony.CellIdentityGsm sanitizeLocationInfo();
   }
 
   public final class CellIdentityLte extends android.telephony.CellIdentity {
     method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
+    method @NonNull public android.telephony.CellIdentityLte sanitizeLocationInfo();
   }
 
   public final class CellIdentityNr extends android.telephony.CellIdentity {
     method @NonNull public android.telephony.CellLocation asCellLocation();
+    method @NonNull public android.telephony.CellIdentityNr sanitizeLocationInfo();
   }
 
   public final class CellIdentityTdscdma extends android.telephony.CellIdentity {
     method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
+    method @NonNull public android.telephony.CellIdentityTdscdma sanitizeLocationInfo();
   }
 
   public final class CellIdentityWcdma extends android.telephony.CellIdentity {
     method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
+    method @NonNull public android.telephony.CellIdentityWcdma sanitizeLocationInfo();
   }
 
   public final class DataFailCause {
@@ -10211,10 +10399,10 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoiceActivationState();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmi(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmiForSubscriber(int, String);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int);
     method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String);
-    method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String);
+    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
@@ -10381,6 +10569,20 @@
     method public static final void setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings);
   }
 
+  public final class WapPushManagerConnector {
+    ctor public WapPushManagerConnector(@NonNull android.content.Context);
+    method public boolean bindToWapPushManagerService();
+    method @Nullable public String getConnectedWapPushManagerServicePackage();
+    method public int processMessage(@NonNull String, @NonNull String, @NonNull android.content.Intent);
+    method public void unbindWapPushManagerService();
+    field public static final int RESULT_APP_QUERY_FAILED = 2; // 0x2
+    field public static final int RESULT_EXCEPTION_CAUGHT = 16; // 0x10
+    field public static final int RESULT_FURTHER_PROCESSING = 32768; // 0x8000
+    field public static final int RESULT_INVALID_RECEIVER_NAME = 8; // 0x8
+    field public static final int RESULT_MESSAGE_HANDLED = 1; // 0x1
+    field public static final int RESULT_SIGNATURE_NO_MATCH = 4; // 0x4
+  }
+
 }
 
 package android.telephony.cdma {
@@ -11631,6 +11833,8 @@
   public final class AccessibilityManager {
     method public int getAccessibilityWindowId(@Nullable android.os.IBinder);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void registerSystemAction(@NonNull android.app.RemoteAction, int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int);
   }
 
 }
diff --git a/api/test-current.txt b/api/test-current.txt
index 9967942..d017dd6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -437,6 +437,7 @@
   public class StatusBarManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
+    method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
   }
 
   public static final class StatusBarManager.DisableInfo {
@@ -2551,6 +2552,7 @@
     field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
     field public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION = "android.settings.MANAGE_APP_OVERLAY_PERMISSION";
     field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
+    field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI";
     field public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; // 0x1
   }
 
@@ -2569,6 +2571,7 @@
     field public static final String LOW_POWER_MODE_STICKY = "low_power_sticky";
     field public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
     field public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices";
+    field public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
     field public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
   }
 
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 7b96ce9..118a508 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -126,29 +126,28 @@
     ],
 
     static_libs: [
-        "libhealthhalutils",
-        "libplatformprotos",
-    ],
-
-    shared_libs: [
         "android.frameworks.stats@1.0",
-        "android.hardware.health@2.0",
         "android.hardware.power.stats@1.0",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
         "libbase",
-        "libbinder",
         "libcutils",
+        "libhealthhalutils",
+        "liblog",
+        "libplatformprotos",
+        "libprotoutil",
+        "libstatslog",
+        "libstatssocket",
+        "libsysutils",
+    ],
+    shared_libs: [
+        "android.hardware.health@2.0",
+        "libbinder",
         "libgraphicsenv",
         "libhidlbase",
         "libincident",
-        "liblog",
-        "libprotoutil",
         "libservices",
-        "libstatslog",
         "libstatsmetadata",
-        "libstatssocket",
-        "libsysutils",
         "libtimestats_proto",
         "libutils",
     ],
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 5ff0f97..1ca19c3 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1135,12 +1135,11 @@
     }
 }
 
-Status StatsService::getData(int64_t key, const String16& packageName, vector<uint8_t>* output) {
-    ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::getData(int64_t key, const int32_t callingUid, vector<uint8_t>* output) {
+    ENFORCE_UID(AID_SYSTEM);
 
-    IPCThreadState* ipc = IPCThreadState::self();
-    VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
-    ConfigKey configKey(ipc->getCallingUid(), key);
+    VLOG("StatsService::getData with Uid %i", callingUid);
+    ConfigKey configKey(callingUid, key);
     // The dump latency does not matter here since we do not include the current bucket, we do not
     // need to pull any new data anyhow.
     mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/,
@@ -1148,22 +1147,18 @@
     return Status::ok();
 }
 
-Status StatsService::getMetadata(const String16& packageName, vector<uint8_t>* output) {
-    ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::getMetadata(vector<uint8_t>* output) {
+    ENFORCE_UID(AID_SYSTEM);
 
-    IPCThreadState* ipc = IPCThreadState::self();
-    VLOG("StatsService::getMetadata with Pid %i, Uid %i", ipc->getCallingPid(),
-         ipc->getCallingUid());
     StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
     return Status::ok();
 }
 
 Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config,
-                                      const String16& packageName) {
-    ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+                                      const int32_t callingUid) {
+    ENFORCE_UID(AID_SYSTEM);
 
-    IPCThreadState* ipc = IPCThreadState::self();
-    if (addConfigurationChecked(ipc->getCallingUid(), key, config)) {
+    if (addConfigurationChecked(callingUid, key, config)) {
         return Status::ok();
     } else {
         ALOGE("Could not parse malformatted StatsdConfig");
@@ -1228,13 +1223,11 @@
     return Status::ok();
 }
 
-Status StatsService::removeConfiguration(int64_t key, const String16& packageName) {
-    ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::removeConfiguration(int64_t key, const int32_t callingUid) {
+    ENFORCE_UID(AID_SYSTEM);
 
-    IPCThreadState* ipc = IPCThreadState::self();
-    ConfigKey configKey(ipc->getCallingUid(), key);
+    ConfigKey configKey(callingUid, key);
     mConfigManager->RemoveConfig(configKey);
-    SubscriberReporter::getInstance().removeConfig(configKey);
     return Status::ok();
 }
 
@@ -1477,17 +1470,7 @@
 
 
 Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) {
-    uid_t uid = IPCThreadState::self()->getCallingUid();
-
-    // Caller must be granted these permissions
-    if (!checkCallingPermission(String16(kPermissionDump))) {
-        return exception(binder::Status::EX_SECURITY,
-                         StringPrintf("UID %d lacks permission %s", uid, kPermissionDump));
-    }
-    if (!checkCallingPermission(String16(kPermissionUsage))) {
-        return exception(binder::Status::EX_SECURITY,
-                         StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage));
-    }
+    ENFORCE_UID(AID_SYSTEM);
     // TODO: add verifier permission
 
     // Read the latest train info
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 0565f3c..c9a9072 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -99,15 +99,14 @@
      * Binder call for clients to request data for this configuration key.
      */
     virtual Status getData(int64_t key,
-                           const String16& packageName,
+                           const int32_t callingUid,
                            vector<uint8_t>* output) override;
 
 
     /**
      * Binder call for clients to get metadata across all configs in statsd.
      */
-    virtual Status getMetadata(const String16& packageName,
-                               vector<uint8_t>* output) override;
+    virtual Status getMetadata(vector<uint8_t>* output) override;
 
 
     /**
@@ -116,7 +115,7 @@
      */
     virtual Status addConfiguration(int64_t key,
                                     const vector<uint8_t>& config,
-                                    const String16& packageName) override;
+                                    const int32_t callingUid) override;
 
     /**
      * Binder call to let clients register the data fetch operation for a configuration.
@@ -146,7 +145,7 @@
      * Binder call to allow clients to remove the specified configuration.
      */
     virtual Status removeConfiguration(int64_t key,
-                                       const String16& packageName) override;
+                                       const int32_t callingUid) override;
 
     /**
      * Binder call to associate the given config's subscriberId with the given pendingIntentRef.
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 55d73c1..972adf7 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -189,25 +189,11 @@
             // Remove from map
             uidIt->second.erase(key);
 
-            // No more configs for this uid, lets remove the active configs callback.
-            if (uidIt->second.empty()) {
-                auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid);
-                    if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) {
-                        mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver);
-                    }
-            }
-
             for (const sp<ConfigListener>& listener : mListeners) {
                 broadcastList.push_back(listener);
             }
         }
 
-        auto itReceiver = mConfigReceivers.find(key);
-        if (itReceiver != mConfigReceivers.end()) {
-            // Remove from map
-            mConfigReceivers.erase(itReceiver);
-        }
-
         // Remove from disk. There can still be a lingering file on disk so we check
         // whether or not the config was on memory.
         remove_saved_configs(key);
@@ -238,12 +224,6 @@
             // Remove from map
                 remove_saved_configs(*it);
                 removed.push_back(*it);
-                mConfigReceivers.erase(*it);
-        }
-
-        auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid);
-        if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) {
-            mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver);
         }
 
         mConfigs.erase(uidIt);
@@ -277,8 +257,6 @@
             uidIt = mConfigs.erase(uidIt);
         }
 
-        mConfigReceivers.clear();
-        mActiveConfigsChangedReceivers.clear();
         for (const sp<ConfigListener>& listener : mListeners) {
             broadcastList.push_back(listener);
         }
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 35c6d37..e85b975 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -207,10 +207,7 @@
                                      &linkedConditionDimensionKey);
             if (trueConditionDimensions.find(linkedConditionDimensionKey) !=
                     trueConditionDimensions.end()) {
-                for (auto& condIt : whatIt.second) {
-                    condIt.second->onConditionChanged(
-                            currentUnSlicedPartCondition, eventTime);
-                }
+                whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime);
             }
         }
     } else {
@@ -222,15 +219,11 @@
                                          &linkedConditionDimensionKey);
                 if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) !=
                         dimensionsChangedToTrue->end()) {
-                    for (auto& condIt : whatIt.second) {
-                        condIt.second->onConditionChanged(true, eventTime);
-                    }
+                    whatIt.second->onConditionChanged(true, eventTime);
                 }
                 if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) !=
                         dimensionsChangedToFalse->end()) {
-                    for (auto& condIt : whatIt.second) {
-                        condIt.second->onConditionChanged(false, eventTime);
-                    }
+                    whatIt.second->onConditionChanged(false, eventTime);
                 }
             }
         }
@@ -247,9 +240,7 @@
 
     // Now for each of the on-going event, check if the condition has changed for them.
     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
-        for (auto& pair : whatIt.second) {
-            pair.second->onSlicedConditionMayChange(overallCondition, eventTimeNs);
-        }
+        whatIt.second->onSlicedConditionMayChange(overallCondition, eventTimeNs);
     }
 }
 
@@ -283,18 +274,14 @@
         }
 
         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
-            for (auto& pair : whatIt.second) {
-                pair.second->onConditionChanged(mIsActive, eventTimeNs);
-            }
+            whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
         }
     } else if (mIsActive) {
         flushIfNeededLocked(eventTimeNs);
         onSlicedConditionMayChangeInternalLocked(mIsActive, eventTimeNs);
     } else { // mConditionSliced == true && !mIsActive
         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
-            for (auto& pair : whatIt.second) {
-                pair.second->onConditionChanged(mIsActive, eventTimeNs);
-            }
+            whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
         }
     }
 }
@@ -310,9 +297,7 @@
 
     flushIfNeededLocked(eventTime);
     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
-        for (auto& pair : whatIt.second) {
-            pair.second->onConditionChanged(conditionMet, eventTime);
-        }
+        whatIt.second->onConditionChanged(conditionMet, eventTime);
     }
 }
 
@@ -425,19 +410,11 @@
                                                       const int64_t& nextBucketStartTimeNs) {
     for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
             whatIt != mCurrentSlicedDurationTrackerMap.end();) {
-        for (auto it = whatIt->second.begin(); it != whatIt->second.end();) {
-            if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
-                VLOG("erase bucket for key %s %s", whatIt->first.toString().c_str(),
-                     it->first.toString().c_str());
-                it = whatIt->second.erase(it);
-            } else {
-                ++it;
-            }
-        }
-        if (whatIt->second.empty()) {
+        if (whatIt->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
+            VLOG("erase bucket for key %s", whatIt->first.toString().c_str());
             whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
         } else {
-            whatIt++;
+            ++whatIt;
         }
     }
     StatsdStats::getInstance().noteBucketCount(mMetricId);
@@ -453,35 +430,15 @@
             (unsigned long)mCurrentSlicedDurationTrackerMap.size());
     if (verbose) {
         for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
-            for (const auto& slice : whatIt.second) {
-                fprintf(out, "\t(what)%s\t(states)%s\n", whatIt.first.toString().c_str(),
-                        slice.first.toString().c_str());
-                slice.second->dumpStates(out, verbose);
-            }
+            fprintf(out, "\t(what)%s\n", whatIt.first.toString().c_str());
+            whatIt.second->dumpStates(out, verbose);
         }
     }
 }
 
 bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
     auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat());
-    if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
-        auto stateIt = whatIt->second.find(newKey.getStateValuesKey());
-        if (stateIt != whatIt->second.end()) {
-            return false;
-        }
-        if (whatIt->second.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
-            size_t newTupleCount = whatIt->second.size() + 1;
-            StatsdStats::getInstance().noteMetricDimensionInConditionSize(
-                    mConfigKey, mMetricId, newTupleCount);
-            // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
-            if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-                ALOGE("DurationMetric %lld dropping data for state values key %s",
-                      (long long)mMetricId, newKey.getStateValuesKey().toString().c_str());
-                StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
-                return true;
-            }
-        }
-    } else {
+    if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
         // 1. Report the tuple count if the tuple count > soft limit
         if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
             size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
@@ -503,24 +460,16 @@
                                               const ConditionKey& conditionKeys,
                                               bool condition, const LogEvent& event) {
     const auto& whatKey = eventKey.getDimensionKeyInWhat();
-    const auto& stateKey = eventKey.getStateValuesKey();
 
     auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
     if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
         if (hitGuardRailLocked(eventKey)) {
             return;
         }
-        mCurrentSlicedDurationTrackerMap[whatKey][stateKey] = createDurationTracker(eventKey);
-    } else {
-        if (whatIt->second.find(stateKey) == whatIt->second.end()) {
-            if (hitGuardRailLocked(eventKey)) {
-                return;
-            }
-            mCurrentSlicedDurationTrackerMap[whatKey][stateKey] = createDurationTracker(eventKey);
-        }
+        mCurrentSlicedDurationTrackerMap[whatKey] = createDurationTracker(eventKey);
     }
 
-    auto it = mCurrentSlicedDurationTrackerMap.find(whatKey)->second.find(stateKey);
+    auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
     if (mUseWhatDimensionAsInternalDimension) {
         it->second->noteStart(whatKey, condition,
                               event.GetElapsedTimestampNs(), conditionKeys);
@@ -560,18 +509,14 @@
     // Handles Stopall events.
     if (matcherIndex == mStopAllIndex) {
         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
-            for (auto& pair : whatIt.second) {
-                pair.second->noteStopAll(event.GetElapsedTimestampNs());
-            }
+            whatIt.second->noteStopAll(event.GetElapsedTimestampNs());
         }
         return;
     }
 
-    HashableDimensionKey dimensionInWhat;
+    HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY;
     if (!mDimensionsInWhat.empty()) {
         filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
-    } else {
-       dimensionInWhat = DEFAULT_DIMENSION_KEY;
     }
 
     // Handles Stop events.
@@ -579,9 +524,7 @@
         if (mUseWhatDimensionAsInternalDimension) {
             auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
             if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
-                for (const auto& stateIt : whatIt->second) {
-                    stateIt.second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
-                }
+                whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
             }
             return;
         }
@@ -593,10 +536,7 @@
 
         auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
         if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
-            for (const auto& stateIt : whatIt->second) {
-                stateIt.second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(),
-                                         false);
-            }
+            whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false);
         }
         return;
     }
@@ -619,8 +559,8 @@
 
     condition = condition && mIsActive;
 
-    handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY),
-                     conditionKey, condition, event);
+    handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY), conditionKey,
+                     condition, event);
 }
 
 size_t DurationMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 45908fb..06da0f6 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -132,8 +132,7 @@
     std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
 
     // The duration trackers in the current bucket.
-    std::unordered_map<HashableDimensionKey,
-        std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>>
+    std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
             mCurrentSlicedDurationTrackerMap;
 
     // Helper function to create a duration tracker given the metric aggregation type.
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 6b5c299..afe93d4 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -82,8 +82,6 @@
 
     virtual ~DurationTracker(){};
 
-    virtual unique_ptr<DurationTracker> clone(const int64_t eventTime) = 0;
-
     virtual void noteStart(const HashableDimensionKey& key, bool condition,
                            const int64_t eventTime, const ConditionKey& conditionKey) = 0;
     virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index df66cb0..2be5855 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -37,24 +37,6 @@
                       conditionSliced, fullLink, anomalyTrackers) {
 }
 
-unique_ptr<DurationTracker> MaxDurationTracker::clone(const int64_t eventTime) {
-    auto clonedTracker = make_unique<MaxDurationTracker>(*this);
-    for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end();) {
-        if (it->second.state  != kStopped) {
-            it->second.lastStartTime = eventTime;
-            it->second.lastDuration = 0;
-            it++;
-        } else {
-            it = clonedTracker->mInfos.erase(it);
-        }
-    }
-    if (clonedTracker->mInfos.empty()) {
-        return nullptr;
-    } else {
-        return clonedTracker;
-    }
-}
-
 bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
     // ===========GuardRail==============
     if (mInfos.find(newKey) != mInfos.end()) {
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index d0371da..efb8dc7 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -38,8 +38,6 @@
 
     MaxDurationTracker(const MaxDurationTracker& tracker) = default;
 
-    unique_ptr<DurationTracker> clone(const int64_t eventTime) override;
-
     void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
                    const ConditionKey& conditionKey) override;
     void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index b0fd975..57f3965 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -38,13 +38,6 @@
     mLastStartTime = 0;
 }
 
-unique_ptr<DurationTracker> OringDurationTracker::clone(const int64_t eventTime) {
-    auto clonedTracker = make_unique<OringDurationTracker>(*this);
-    clonedTracker->mLastStartTime = eventTime;
-    clonedTracker->mDuration = 0;
-    return clonedTracker;
-}
-
 bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
     // ===========GuardRail==============
     // 1. Report the tuple count if the tuple count > soft limit
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index 43c48d5..c3aad66 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -37,8 +37,6 @@
 
     OringDurationTracker(const OringDurationTracker& tracker) = default;
 
-    unique_ptr<DurationTracker> clone(const int64_t eventTime) override;
-
     void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
                    const ConditionKey& conditionKey) override;
     void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index a9a105f..a37cad1 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -69,12 +69,6 @@
     }
 }
 
-void SubscriberReporter::removeConfig(const ConfigKey& configKey) {
-    VLOG("SubscriberReporter::removeConfig called.");
-    lock_guard<std::mutex> lock(mLock);
-    mIntentMap.erase(configKey);
-}
-
 void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
                                                   const Subscription& subscription,
                                                   const MetricDimensionKey& dimKey) const {
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 8ccc8ee..087a1b8 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -59,9 +59,6 @@
      */
     void unsetBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
 
-    /** Remove all information stored by SubscriberReporter about the given config. */
-    void removeConfig(const ConfigKey& configKey);
-
     /**
      * Sends a broadcast via the intentSender previously stored for the
      * given (configKey, subscriberId) pair by setBroadcastSubscriber.
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 0bc3ebb..16b51d9 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -28,16 +28,16 @@
 
 #ifdef __ANDROID__
 
-const string kAndroid = "android";
 const string kApp1 = "app1.sharing.1";
 const int kConfigKey = 789130123;  // Randomly chosen to avoid collisions with existing configs.
+const int kCallingUid = 0; // Randomly chosen
 
 void SendConfig(StatsService& service, const StatsdConfig& config) {
     string str;
     config.SerializeToString(&str);
     std::vector<uint8_t> configAsVec(str.begin(), str.end());
     bool success;
-    service.addConfiguration(kConfigKey, configAsVec, String16(kAndroid.c_str()));
+    service.addConfiguration(kConfigKey, configAsVec, kCallingUid);
 }
 
 ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp,
@@ -50,7 +50,7 @@
     ConfigMetricsReportList reports;
     reports.ParseFromArray(output.data(), output.size());
     EXPECT_EQ(1, reports.reports_size());
-    return reports.reports(0);
+    return reports.reports(kCallingUid);
 }
 
 StatsdConfig MakeConfig() {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a4f6f57..b82a675 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3349,8 +3349,8 @@
     }
 
     @Override
-    public void handleStartActivity(ActivityClientRecord r,
-            PendingTransactionActions pendingActions) {
+    public void handleStartActivity(IBinder token, PendingTransactionActions pendingActions) {
+        final ActivityClientRecord r = mActivities.get(token);
         final Activity activity = r.activity;
         if (r.activity == null) {
             // TODO(lifecycler): What do we do in this case?
@@ -3364,6 +3364,8 @@
             return;
         }
 
+        unscheduleGcIdler();
+
         // Start
         activity.performStart("handleStartActivity");
         r.setState(ON_START);
@@ -3400,6 +3402,9 @@
                                 + " did not call through to super.onPostCreate()");
             }
         }
+
+        updateVisibility(r, true /* show */);
+        mSomeActivitiesChanged = true;
     }
 
     /**
@@ -4660,8 +4665,8 @@
     @UnsupportedAppUsage
     final void performStopActivity(IBinder token, boolean saveState, String reason) {
         ActivityClientRecord r = mActivities.get(token);
-        performStopActivityInner(r, null /* stopInfo */, false /* keepShown */, saveState,
-                false /* finalStateRequest */, reason);
+        performStopActivityInner(r, null /* stopInfo */, saveState, false /* finalStateRequest */,
+                reason);
     }
 
     private static final class ProviderRefCount {
@@ -4687,25 +4692,19 @@
     }
 
     /**
-     * Core implementation of stopping an activity.  Note this is a little
-     * tricky because the server's meaning of stop is slightly different
-     * than our client -- for the server, stop means to save state and give
-     * it the result when it is done, but the window may still be visible.
-     * For the client, we want to call onStop()/onStart() to indicate when
-     * the activity's UI visibility changes.
+     * Core implementation of stopping an activity.
      * @param r Target activity client record.
      * @param info Action that will report activity stop to server.
-     * @param keepShown Flag indicating whether the activity is still shown.
      * @param saveState Flag indicating whether the activity state should be saved.
      * @param finalStateRequest Flag indicating if this call is handling final lifecycle state
      *                          request for a transaction.
      * @param reason Reason for performing this operation.
      */
-    private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown,
+    private void performStopActivityInner(ActivityClientRecord r, StopInfo info,
             boolean saveState, boolean finalStateRequest, String reason) {
         if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
         if (r != null) {
-            if (!keepShown && r.stopped) {
+            if (r.stopped) {
                 if (r.activity.mFinished) {
                     // If we are finishing, we won't call onResume() in certain
                     // cases.  So here we likewise don't want to call onStop()
@@ -4740,9 +4739,7 @@
                 }
             }
 
-            if (!keepShown) {
-                callActivityOnStop(r, saveState, reason);
-            }
+            callActivityOnStop(r, saveState, reason);
         }
     }
 
@@ -4810,20 +4807,19 @@
     }
 
     @Override
-    public void handleStopActivity(IBinder token, boolean show, int configChanges,
+    public void handleStopActivity(IBinder token, int configChanges,
             PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
         final ActivityClientRecord r = mActivities.get(token);
         r.activity.mConfigChangeFlags |= configChanges;
 
         final StopInfo stopInfo = new StopInfo();
-        performStopActivityInner(r, stopInfo, show, true /* saveState */, finalStateRequest,
+        performStopActivityInner(r, stopInfo, true /* saveState */, finalStateRequest,
                 reason);
 
         if (localLOGV) Slog.v(
-            TAG, "Finishing stop of " + r + ": show=" + show
-            + " win=" + r.window);
+            TAG, "Finishing stop of " + r + ": win=" + r.window);
 
-        updateVisibility(r, show);
+        updateVisibility(r, false);
 
         // Make sure any pending writes are now committed.
         if (!r.isPreHoneycomb()) {
@@ -4859,34 +4855,6 @@
         }
     }
 
-    @Override
-    public void handleWindowVisibility(IBinder token, boolean show) {
-        ActivityClientRecord r = mActivities.get(token);
-
-        if (r == null) {
-            Log.w(TAG, "handleWindowVisibility: no activity for token " + token);
-            return;
-        }
-
-        if (!show && !r.stopped) {
-            performStopActivityInner(r, null /* stopInfo */, show, false /* saveState */,
-                    false /* finalStateRequest */, "handleWindowVisibility");
-        } else if (show && r.getLifecycleState() == ON_STOP) {
-            // If we are getting ready to gc after going to the background, well
-            // we are back active so skip it.
-            unscheduleGcIdler();
-
-            r.activity.performRestart(true /* start */, "handleWindowVisibility");
-            r.setState(ON_START);
-        }
-        if (r.activity.mDecor != null) {
-            if (false) Slog.v(
-                TAG, "Handle window " + r + " visibility: " + show);
-            updateVisibility(r, show);
-        }
-        mSomeActivitiesChanged = true;
-    }
-
     // TODO: This method should be changed to use {@link #performStopActivityInner} to perform to
     // stop operation on the activity to reduce code duplication and the chance of fixing a bug in
     // one place and missing the other.
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index f9a689a..d2235f1 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -119,7 +119,6 @@
     /**
      * Stop the activity.
      * @param token Target activity token.
-     * @param show Flag indicating whether activity is still shown.
      * @param configChanges Activity configuration changes.
      * @param pendingActions Pending actions to be used on this or later stages of activity
      *                       transaction.
@@ -127,7 +126,7 @@
      *                          request for a transaction.
      * @param reason Reason for performing this operation.
      */
-    public abstract void handleStopActivity(IBinder token, boolean show, int configChanges,
+    public abstract void handleStopActivity(IBinder token, int configChanges,
             PendingTransactionActions pendingActions, boolean finalStateRequest, String reason);
 
     /** Report that activity was stopped to server. */
@@ -161,15 +160,12 @@
     /** Request that an activity enter picture-in-picture. */
     public abstract void handlePictureInPictureRequested(IBinder token);
 
-    /** Update window visibility. */
-    public abstract void handleWindowVisibility(IBinder token, boolean show);
-
     /** Perform activity launch. */
     public abstract Activity handleLaunchActivity(ActivityThread.ActivityClientRecord r,
             PendingTransactionActions pendingActions, Intent customIntent);
 
     /** Perform activity start. */
-    public abstract void handleStartActivity(ActivityThread.ActivityClientRecord r,
+    public abstract void handleStartActivity(IBinder token,
             PendingTransactionActions pendingActions);
 
     /** Get package info. */
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 49c389a..1278ff6 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -16,7 +16,9 @@
 
 package android.app;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
@@ -40,11 +42,13 @@
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.provider.BaseColumns;
 import android.provider.Downloads;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
+import android.util.LongSparseArray;
 import android.util.Pair;
 
 import java.io.File;
@@ -1069,6 +1073,37 @@
     }
 
     /**
+     * Notify {@link DownloadManager} that the given {@link MediaStore} items
+     * were just deleted so that {@link DownloadManager} internal data
+     * structures can be cleaned up.
+     *
+     * @param idToMime map from {@link BaseColumns#_ID} to
+     *            {@link ContentResolver#getType(Uri)}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE)
+    public void onMediaStoreDownloadsDeleted(@NonNull LongSparseArray<String> idToMime) {
+        try (ContentProviderClient client = mResolver
+                .acquireUnstableContentProviderClient(mBaseUri)) {
+           final Bundle callExtras = new Bundle();
+           final long[] ids = new long[idToMime.size()];
+           final String[] mimeTypes = new String[idToMime.size()];
+           for (int i = idToMime.size() - 1; i >= 0; --i) {
+               ids[i] = idToMime.keyAt(i);
+               mimeTypes[i] = idToMime.valueAt(i);
+           }
+           callExtras.putLongArray(android.provider.Downloads.EXTRA_IDS, ids);
+           callExtras.putStringArray(android.provider.Downloads.EXTRA_MIME_TYPES,
+                   mimeTypes);
+           client.call(android.provider.Downloads.CALL_MEDIASTORE_DOWNLOADS_DELETED,
+                   null, callExtras);
+        } catch (RemoteException e) {
+            // Should not happen
+        }
+    }
+
+    /**
      * Enqueue a new download.  The download will start automatically once the download manager is
      * ready to execute it and connectivity is available.
      *
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 4033aea..7cdf85e 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -177,7 +177,7 @@
                 pendingActions = null;
             }
 
-            mActivityThread.handleStartActivity(clientRecord, pendingActions);
+            mActivityThread.handleStartActivity(r, pendingActions);
             r.curState = STARTED;
             
             if (desiredState == RESUMED) {
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 83d1de6..8426374 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -157,11 +157,11 @@
     public void addConfig(long configKey, byte[] config) throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsd service = getIStatsdLocked();
+                IStatsManagerService service = getIStatsManagerServiceLocked();
                 // can throw IllegalArgumentException
                 service.addConfiguration(configKey, config, mContext.getOpPackageName());
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsd when adding configuration");
+                Slog.e(TAG, "Failed to connect to statsmanager when adding configuration");
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
                 throw new StatsUnavailableException(e.getMessage(), e);
@@ -194,10 +194,10 @@
     public void removeConfig(long configKey) throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsd service = getIStatsdLocked();
+                IStatsManagerService service = getIStatsManagerServiceLocked();
                 service.removeConfiguration(configKey, mContext.getOpPackageName());
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsd when removing configuration");
+                Slog.e(TAG, "Failed to connect to statsmanager when removing configuration");
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
                 throw new StatsUnavailableException(e.getMessage(), e);
@@ -390,10 +390,10 @@
     public byte[] getReports(long configKey) throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsd service = getIStatsdLocked();
+                IStatsManagerService service = getIStatsManagerServiceLocked();
                 return service.getData(configKey, mContext.getOpPackageName());
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsd when getting data");
+                Slog.e(TAG, "Failed to connect to statsmanager when getting data");
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
                 throw new StatsUnavailableException(e.getMessage(), e);
@@ -427,10 +427,10 @@
     public byte[] getStatsMetadata() throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsd service = getIStatsdLocked();
+                IStatsManagerService service = getIStatsManagerServiceLocked();
                 return service.getMetadata(mContext.getOpPackageName());
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsd when getting metadata");
+                Slog.e(TAG, "Failed to connect to statsmanager when getting metadata");
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
                 throw new StatsUnavailableException(e.getMessage(), e);
@@ -462,21 +462,19 @@
             throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsd service = getIStatsdLocked();
+                IStatsManagerService service = getIStatsManagerServiceLocked();
                 if (service == null) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Failed to find statsd when getting experiment IDs");
-                    }
-                    return new long[0];
+                    throw new StatsUnavailableException("Failed to find statsmanager when "
+                                                              + "getting experiment IDs");
                 }
                 return service.getRegisteredExperimentIds();
             } catch (RemoteException e) {
                 if (DEBUG) {
                     Slog.d(TAG,
-                            "Failed to connect to StatsCompanionService when getting "
+                            "Failed to connect to StatsManagerService when getting "
                                     + "registered experiment IDs");
                 }
-                return new long[0];
+                throw new StatsUnavailableException("could not connect", e);
             }
         }
     }
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index a1765c8..078e453 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -153,6 +153,11 @@
      */
     public static final int DEFAULT_SETUP_DISABLE2_FLAGS = DISABLE2_ROTATE_SUGGESTIONS;
 
+    /**
+     * disable flags to be applied when the device is sim-locked.
+     */
+    private static final int DEFAULT_SIM_LOCKED_DISABLED_FLAGS = DISABLE_EXPAND;
+
     /** @hide */
     public static final int NAVIGATION_HINT_BACK_ALT      = 1 << 0;
     /** @hide */
@@ -385,6 +390,30 @@
     }
 
     /**
+     * Enable or disable expansion of the status bar. When the device is SIM-locked, the status
+     * bar should not be expandable.
+     *
+     * @param disabled If {@code true}, the status bar will be set to non-expandable. If
+     *                 {@code false}, re-enables expansion of the status bar.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.STATUS_BAR)
+    public void setDisabledForSimNetworkLock(boolean disabled) {
+        try {
+            final int userId = Binder.getCallingUserHandle().getIdentifier();
+            final IStatusBarService svc = getService();
+            if (svc != null) {
+                svc.disableForUser(disabled ? DEFAULT_SIM_LOCKED_DISABLED_FLAGS : DISABLE_NONE,
+                        mToken, mContext.getPackageName(), userId);
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Get this app's currently requested disabled components
      *
      * @return a new DisableInfo
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f3028a1..2aac94c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -88,7 +88,6 @@
 import android.util.ArraySet;
 import android.util.Log;
 
-import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.Preconditions;
@@ -6757,6 +6756,34 @@
     }
 
     /**
+     * @hide
+     * Privileged apps can use this method to find out if the device was provisioned as
+     * organization-owend device with a managed profile.
+     *
+     * This, together with checking whether the device has a device owner (by calling
+     * {@link #isDeviceManaged()}), could be used to learn whether the device is owned by an
+     * organization or an individual:
+     * If this method returns true OR {@link #isDeviceManaged()} returns true, then
+     * the device is owned by an organization. Otherwise, it's owned by an individual.
+     *
+     * @return {@code true} if the device was provisioned as organization-owned device,
+     * {@code false} otherwise.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public boolean isOrganizationOwnedDeviceWithManagedProfile() {
+        throwIfParentInstance("isOrganizationOwnedDeviceWithManagedProfile");
+        if (mService != null) {
+            try {
+                return mService.isOrganizationOwnedDeviceWithManagedProfile();
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
      * Returns whether the specified package can read the device identifiers.
      *
      * @param packageName The package name of the app to check for device identifier access.
@@ -11215,12 +11242,12 @@
      * #setCrossProfilePackages(ComponentName, Set)}.</li>
      * <li>The default package names set by the OEM that are allowed to request user consent for
      * cross-profile communication without being explicitly enabled by the admin, via
-     * {@link R.array#cross_profile_apps}</li>
+     * {@link com.android.internal.R.array#cross_profile_apps}</li>
      * </ul>
      *
      * @return the combined set of whitelisted package names set via
      * {@link #setCrossProfilePackages(ComponentName, Set)} and
-     * {@link R.array#cross_profile_apps}
+     * {@link com.android.internal.R.array#cross_profile_apps}
      *
      * @hide
      */
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 55dfe2f..3eec46b 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -156,6 +156,7 @@
     void setProfileName(in ComponentName who, String profileName);
     void clearProfileOwner(in ComponentName who);
     boolean hasUserSetupCompleted();
+    boolean isOrganizationOwnedDeviceWithManagedProfile();
 
     boolean checkDeviceIdentifierAccess(in String packageName, int pid, int uid);
 
diff --git a/core/java/android/app/servertransaction/StartActivityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java
new file mode 100644
index 0000000..4fbe02b
--- /dev/null
+++ b/core/java/android/app/servertransaction/StartActivityItem.java
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+package android.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+
+/**
+ * Request to move an activity to started and visible state.
+ * @hide
+ */
+public class StartActivityItem extends ActivityLifecycleItem {
+
+    private static final String TAG = "StartActivityItem";
+
+    @Override
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "startActivityItem");
+        client.handleStartActivity(token, pendingActions);
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    @Override
+    public int getTargetState() {
+        return ON_START;
+    }
+
+
+    // ObjectPoolItem implementation
+
+    private StartActivityItem() {}
+
+    /** Obtain an instance initialized with provided params. */
+    public static StartActivityItem obtain() {
+        StartActivityItem instance = ObjectPool.obtain(StartActivityItem.class);
+        if (instance == null) {
+            instance = new StartActivityItem();
+        }
+
+        return instance;
+    }
+
+    @Override
+    public void recycle() {
+        super.recycle();
+        ObjectPool.recycle(this);
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        // Empty
+    }
+
+    /** Read from Parcel. */
+    private StartActivityItem(Parcel in) {
+        // Empty
+    }
+
+    public static final @android.annotation.NonNull Creator<StartActivityItem> CREATOR =
+            new Creator<StartActivityItem>() {
+                public StartActivityItem createFromParcel(Parcel in) {
+                    return new StartActivityItem(in);
+                }
+
+                public StartActivityItem[] newArray(int size) {
+                    return new StartActivityItem[size];
+                }
+            };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return 17;
+    }
+
+    @Override
+    public String toString() {
+        return "StartActivityItem{}";
+    }
+}
+
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index 63efa6f..8668bd4 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -31,14 +31,13 @@
 
     private static final String TAG = "StopActivityItem";
 
-    private boolean mShowWindow;
     private int mConfigChanges;
 
     @Override
     public void execute(ClientTransactionHandler client, IBinder token,
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
-        client.handleStopActivity(token, mShowWindow, mConfigChanges, pendingActions,
+        client.handleStopActivity(token, mConfigChanges, pendingActions,
                 true /* finalStateRequest */, "STOP_ACTIVITY_ITEM");
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
@@ -59,13 +58,15 @@
 
     private StopActivityItem() {}
 
-    /** Obtain an instance initialized with provided params. */
-    public static StopActivityItem obtain(boolean showWindow, int configChanges) {
+    /**
+     * Obtain an instance initialized with provided params.
+     * @param configChanges Configuration pieces that changed.
+     */
+    public static StopActivityItem obtain(int configChanges) {
         StopActivityItem instance = ObjectPool.obtain(StopActivityItem.class);
         if (instance == null) {
             instance = new StopActivityItem();
         }
-        instance.mShowWindow = showWindow;
         instance.mConfigChanges = configChanges;
 
         return instance;
@@ -74,7 +75,6 @@
     @Override
     public void recycle() {
         super.recycle();
-        mShowWindow = false;
         mConfigChanges = 0;
         ObjectPool.recycle(this);
     }
@@ -85,13 +85,11 @@
     /** Write to Parcel. */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeBoolean(mShowWindow);
         dest.writeInt(mConfigChanges);
     }
 
     /** Read from Parcel. */
     private StopActivityItem(Parcel in) {
-        mShowWindow = in.readBoolean();
         mConfigChanges = in.readInt();
     }
 
@@ -115,20 +113,18 @@
             return false;
         }
         final StopActivityItem other = (StopActivityItem) o;
-        return mShowWindow == other.mShowWindow && mConfigChanges == other.mConfigChanges;
+        return mConfigChanges == other.mConfigChanges;
     }
 
     @Override
     public int hashCode() {
         int result = 17;
-        result = 31 * result + (mShowWindow ? 1 : 0);
         result = 31 * result + mConfigChanges;
         return result;
     }
 
     @Override
     public String toString() {
-        return "StopActivityItem{showWindow=" + mShowWindow + ",configChanges=" + mConfigChanges
-                + "}";
+        return "StopActivityItem{configChanges=" + mConfigChanges + "}";
     }
 }
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 20e0da3..17fcda5 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -218,7 +218,7 @@
                             null /* customIntent */);
                     break;
                 case ON_START:
-                    mTransactionHandler.handleStartActivity(r, mPendingActions);
+                    mTransactionHandler.handleStartActivity(r.token, mPendingActions);
                     break;
                 case ON_RESUME:
                     mTransactionHandler.handleResumeActivity(r.token, false /* finalStateRequest */,
@@ -230,8 +230,8 @@
                             "LIFECYCLER_PAUSE_ACTIVITY");
                     break;
                 case ON_STOP:
-                    mTransactionHandler.handleStopActivity(r.token, false /* show */,
-                            0 /* configChanges */, mPendingActions, false /* finalStateRequest */,
+                    mTransactionHandler.handleStopActivity(r.token, 0 /* configChanges */,
+                            mPendingActions, false /* finalStateRequest */,
                             "LIFECYCLER_STOP_ACTIVITY");
                     break;
                 case ON_DESTROY:
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 0ea8c3c..6df92a7 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -183,8 +183,7 @@
                 lifecycleItem = PauseActivityItem.obtain();
                 break;
             case ON_STOP:
-                lifecycleItem = StopActivityItem.obtain(r.isVisibleFromServer(),
-                        0 /* configChanges */);
+                lifecycleItem = StopActivityItem.obtain(0 /* configChanges */);
                 break;
             default:
                 lifecycleItem = ResumeActivityItem.obtain(false /* isForward */);
diff --git a/core/java/android/app/servertransaction/WindowVisibilityItem.java b/core/java/android/app/servertransaction/WindowVisibilityItem.java
deleted file mode 100644
index 115d1ec..0000000
--- a/core/java/android/app/servertransaction/WindowVisibilityItem.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2017 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.app.servertransaction;
-
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
-
-import android.app.ClientTransactionHandler;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Trace;
-
-/**
- * Window visibility change message.
- * @hide
- */
-public class WindowVisibilityItem extends ClientTransactionItem {
-
-    private boolean mShowWindow;
-
-    @Override
-    public void execute(ClientTransactionHandler client, IBinder token,
-            PendingTransactionActions pendingActions) {
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
-                mShowWindow ? "activityShowWindow" : "activityHideWindow");
-        client.handleWindowVisibility(token, mShowWindow);
-        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-    }
-
-
-    // ObjectPoolItem implementation
-
-    private WindowVisibilityItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    public static WindowVisibilityItem obtain(boolean showWindow) {
-        WindowVisibilityItem instance = ObjectPool.obtain(WindowVisibilityItem.class);
-        if (instance == null) {
-            instance = new WindowVisibilityItem();
-        }
-        instance.mShowWindow = showWindow;
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        mShowWindow = false;
-        ObjectPool.recycle(this);
-    }
-
-
-    // Parcelable implementation
-
-    /** Write to Parcel. */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeBoolean(mShowWindow);
-    }
-
-    /** Read from Parcel. */
-    private WindowVisibilityItem(Parcel in) {
-        mShowWindow = in.readBoolean();
-    }
-
-    public static final @android.annotation.NonNull Creator<WindowVisibilityItem> CREATOR =
-            new Creator<WindowVisibilityItem>() {
-        public WindowVisibilityItem createFromParcel(Parcel in) {
-            return new WindowVisibilityItem(in);
-        }
-
-        public WindowVisibilityItem[] newArray(int size) {
-            return new WindowVisibilityItem[size];
-        }
-    };
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        final WindowVisibilityItem other = (WindowVisibilityItem) o;
-        return mShowWindow == other.mShowWindow;
-    }
-
-    @Override
-    public int hashCode() {
-        return 17 + 31 * (mShowWindow ? 1 : 0);
-    }
-
-    @Override
-    public String toString() {
-        return "WindowVisibilityItem{showWindow=" + mShowWindow + "}";
-    }
-}
diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java
index 55f92be..50de7385 100644
--- a/core/java/android/app/timedetector/ManualTimeSuggestion.java
+++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
index 4c55ba1..17e9c5a 100644
--- a/core/java/android/app/timedetector/NetworkTimeSuggestion.java
+++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
index 4a89a12..479e4b4 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
 
 import java.util.ArrayList;
 import java.util.Collections;
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index af9ece0..54dd1be 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -24,8 +24,8 @@
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.SystemClock;
+import android.os.TimestampedValue;
 import android.util.Log;
-import android.util.TimestampedValue;
 
 /**
  * The interface through which system components can send signals to the TimeDetectorService.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index b1b6f0d..cb1f055 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2670,6 +2670,9 @@
         } else if (profile == BluetoothProfile.PAN) {
             BluetoothPan pan = new BluetoothPan(context, listener);
             return true;
+        } else if (profile == BluetoothProfile.PBAP) {
+            BluetoothPbap pbap = new BluetoothPbap(context, listener);
+            return true;
         } else if (profile == BluetoothProfile.HEALTH) {
             Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated");
             return false;
@@ -2742,6 +2745,10 @@
                 BluetoothPan pan = (BluetoothPan) proxy;
                 pan.close();
                 break;
+            case BluetoothProfile.PBAP:
+                BluetoothPbap pbap = (BluetoothPbap) proxy;
+                pbap.close();
+                break;
             case BluetoothProfile.GATT:
                 BluetoothGatt gatt = (BluetoothGatt) proxy;
                 gatt.close();
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 393d488..85826fd 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -27,6 +27,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.app.AppOpsManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.PackageManager;
@@ -942,7 +943,18 @@
         return null;
     }
 
-    /** {@hide} */
+    /**
+     * Return the package name of the caller that initiated the request being
+     * processed on the current thread. The returned package will have
+     * <em>not</em> been verified to belong to the calling UID. Returns
+     * {@code null} if not currently processing a request.
+     * <p>
+     * This will always return {@code null} when processing
+     * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests.
+     *
+     * @see Binder#getCallingUid()
+     * @see Context#grantUriPermission(String, Uri, int)
+     */
     public final @Nullable String getCallingPackageUnchecked() {
         final Pair<String, String> pkg = mCallingPackage.get();
         if (pkg != null) {
@@ -952,7 +964,14 @@
         return null;
     }
 
-    /** {@hide} */
+    /**
+     * Called whenever the value of {@link #getCallingPackage()} changes, giving
+     * the provider an opportunity to invalidate any security related caching it
+     * may be performing.
+     * <p>
+     * This typically happens when a {@link ContentProvider} makes a nested call
+     * back into itself when already processing a call from a remote process.
+     */
     public void onCallingPackageChanged() {
     }
 
@@ -1390,8 +1409,11 @@
      * @param uri The URI to query. This will be the full URI sent by the client.
      * @param projection The list of columns to put into the cursor.
      *            If {@code null} provide a default set of columns.
-     * @param queryArgs A Bundle containing all additional information necessary for the query.
-     *            Values in the Bundle may include SQL style arguments.
+     * @param queryArgs A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
      * @param cancellationSignal A signal to cancel the operation in progress,
      *            or {@code null}.
      * @return a Cursor or {@code null}.
@@ -1525,8 +1547,24 @@
         return false;
     }
 
-    /** {@hide} */
+    /**
+     * Perform a detailed internal check on a {@link Uri} to determine if a UID
+     * is able to access it with specific mode flags.
+     * <p>
+     * This method is typically used when the provider implements more dynamic
+     * access controls that cannot be expressed with {@code <path-permission>}
+     * style static rules.
+     *
+     * @param uri the {@link Uri} to perform an access check on.
+     * @param uid the UID to check the permission for.
+     * @param modeFlags the access flags to use for the access check, such as
+     *            {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}.
+     * @return {@link PackageManager#PERMISSION_GRANTED} if access is allowed,
+     *         otherwise {@link PackageManager#PERMISSION_DENIED}.
+     * @hide
+     */
     @Override
+    @SystemApi
     public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags) {
         return PackageManager.PERMISSION_DENIED;
     }
@@ -1574,9 +1612,14 @@
      *
      * @param uri The content:// URI of the insertion request.
      * @param values A set of column_name/value pairs to add to the database.
-     * @param extras A Bundle containing all additional information necessary
-     *            for the insert.
+     * @param extras A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
      * @return The URI for the newly inserted item.
+     * @throws IllegalArgumentException if the provider doesn't support one of
+     *             the requested Bundle arguments.
      */
     @Override
     public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues values,
@@ -1653,10 +1696,13 @@
      *
      * @param uri The full URI to query, including a row ID (if a specific
      *            record is requested).
-     * @param extras A Bundle containing all additional information necessary
-     *            for the delete. Values in the Bundle may include SQL style
-     *            arguments.
-     * @return The number of rows affected.
+     * @param extras A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
+     * @throws IllegalArgumentException if the provider doesn't support one of
+     *             the requested Bundle arguments.
      * @throws SQLException
      */
     @Override
@@ -1699,10 +1745,14 @@
      * @param uri The URI to query. This can potentially have a record ID if
      *            this is an update request for a specific record.
      * @param values A set of column_name/value pairs to update in the database.
-     * @param extras A Bundle containing all additional information necessary
-     *            for the update. Values in the Bundle may include SQL style
-     *            arguments.
+     * @param extras A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
      * @return the number of rows affected.
+     * @throws IllegalArgumentException if the provider doesn't support one of
+     *             the requested Bundle arguments.
      */
     @Override
     public int update(@NonNull Uri uri, @Nullable ContentValues values,
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index 93f4287..494d2ae 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java
index 11dda83..4fb1ddb 100644
--- a/core/java/android/content/ContentProviderResult.java
+++ b/core/java/android/content/ContentProviderResult.java
@@ -36,7 +36,7 @@
     public final @Nullable Uri uri;
     public final @Nullable Integer count;
     public final @Nullable Bundle extras;
-    public final @Nullable Exception exception;
+    public final @Nullable Throwable exception;
 
     public ContentProviderResult(@NonNull Uri uri) {
         this(Objects.requireNonNull(uri), null, null, null);
@@ -50,12 +50,12 @@
         this(null, null, Objects.requireNonNull(extras), null);
     }
 
-    public ContentProviderResult(@NonNull Exception exception) {
+    public ContentProviderResult(@NonNull Throwable exception) {
         this(null, null, null, exception);
     }
 
     /** {@hide} */
-    public ContentProviderResult(Uri uri, Integer count, Bundle extras, Exception exception) {
+    public ContentProviderResult(Uri uri, Integer count, Bundle extras, Throwable exception) {
         this.uri = uri;
         this.count = count;
         this.extras = extras;
@@ -79,7 +79,7 @@
             extras = null;
         }
         if (source.readInt() != 0) {
-            exception = (Exception) ParcelableException.readFromParcel(source);
+            exception = ParcelableException.readFromParcel(source);
         } else {
             exception = null;
         }
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 1d3c650..6cd1cd3 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -984,7 +984,11 @@
      *         retrieve.
      * @param projection A list of which columns to return. Passing null will
      *         return all columns, which is inefficient.
-     * @param queryArgs A Bundle containing any arguments to the query.
+     * @param queryArgs A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * If the operation is canceled, then {@link OperationCanceledException} will be thrown
      * when the query is executed.
@@ -1925,9 +1929,15 @@
      * @param url The URL of the table to insert into.
      * @param values The initial values for the newly inserted row. The key is the column name for
      *               the field. Passing an empty ContentValues will create an empty row.
-     * @param extras A Bundle containing all additional information necessary for the insert.
+     * @param extras A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
      * @return the URL of the newly created row. May return <code>null</code> if the underlying
      *         content provider returns <code>null</code>, or if it crashes.
+     * @throws IllegalArgumentException if the provider doesn't support one of
+     *             the requested Bundle arguments.
      */
     @Override
     public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
@@ -2061,9 +2071,14 @@
      * If the content provider supports transactions, the deletion will be atomic.
      *
      * @param url The URL of the row to delete.
-     * @param extras A Bundle containing all additional information necessary for the delete.
-     *            Values in the Bundle may include SQL style arguments.
+     * @param extras A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
      * @return The number of rows deleted.
+     * @throws IllegalArgumentException if the provider doesn't support one of
+     *             the requested Bundle arguments.
      */
     @Override
     public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable Bundle extras) {
@@ -2121,10 +2136,15 @@
      * @param uri The URI to modify.
      * @param values The new field values. The key is the column name for the field.
                      A null value will remove an existing field value.
-     * @param extras A Bundle containing all additional information necessary for the update.
-     *            Values in the Bundle may include SQL style arguments.
+     * @param extras A Bundle containing additional information necessary for
+     *            the operation. Arguments may include SQL style arguments, such
+     *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
+     *            the documentation for each individual provider will indicate
+     *            which arguments they support.
      * @return the number of rows updated.
      * @throws NullPointerException if uri or values are null
+     * @throws IllegalArgumentException if the provider doesn't support one of
+     *             the requested Bundle arguments.
      */
     @Override
     public final int update(@RequiresPermission.Write @NonNull Uri uri,
@@ -3851,15 +3871,47 @@
         }
     }
 
+    /**
+     * Decode a path generated by {@link #encodeToFile(Uri)} back into
+     * the original {@link Uri}.
+     * <p>
+     * This is used to offer a way to intercept filesystem calls in
+     * {@link ContentProvider} unaware code and redirect them to a
+     * {@link ContentProvider} when they attempt to use {@code _DATA} columns
+     * that are otherwise deprecated.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static @NonNull Uri decodeFromFile(@NonNull File file) {
+        return translateDeprecatedDataPath(file.getAbsolutePath());
+    }
+
+    /**
+     * Encode a {@link Uri} into an opaque filesystem path which can then be
+     * resurrected by {@link #decodeFromFile(File)}.
+     * <p>
+     * This is used to offer a way to intercept filesystem calls in
+     * {@link ContentProvider} unaware code and redirect them to a
+     * {@link ContentProvider} when they attempt to use {@code _DATA} columns
+     * that are otherwise deprecated.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static @NonNull File encodeToFile(@NonNull Uri uri) {
+        return new File(translateDeprecatedDataPath(uri));
+    }
+
     /** {@hide} */
-    public static Uri translateDeprecatedDataPath(String path) {
+    public static @NonNull Uri translateDeprecatedDataPath(@NonNull String path) {
         final String ssp = "//" + path.substring(DEPRECATE_DATA_PREFIX.length());
         return Uri.parse(new Uri.Builder().scheme(SCHEME_CONTENT)
                 .encodedOpaquePart(ssp).build().toString());
     }
 
     /** {@hide} */
-    public static String translateDeprecatedDataPath(Uri uri) {
+    public static @NonNull String translateDeprecatedDataPath(@NonNull Uri uri) {
         return DEPRECATE_DATA_PREFIX + uri.getEncodedSchemeSpecificPart().substring(2);
     }
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a28868e..5cb2907 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3391,6 +3391,7 @@
             TELEPHONY_SUBSCRIPTION_SERVICE,
             CARRIER_CONFIG_SERVICE,
             EUICC_SERVICE,
+            MMS_SERVICE,
             TELECOM_SERVICE,
             CLIPBOARD_SERVICE,
             INPUT_METHOD_SERVICE,
@@ -3587,6 +3588,8 @@
      * @see android.telephony.CarrierConfigManager
      * @see #EUICC_SERVICE
      * @see android.telephony.euicc.EuiccManager
+     * @see #MMS_SERVICE
+     * @see android.telephony.MmsManager
      * @see #INPUT_METHOD_SERVICE
      * @see android.view.inputmethod.InputMethodManager
      * @see #UI_MODE_SERVICE
@@ -4016,6 +4019,7 @@
      * @see android.net.wifi.WifiCondManager
      * @hide
      */
+    @SystemApi
     public static final String WIFI_COND_SERVICE = "wificond";
 
     /**
@@ -4262,6 +4266,15 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.telephony.MmsManager} to send/receive MMS messages.
+     *
+     * @see #getSystemService(String)
+     * @see android.telephony.MmsManager
+     */
+    public static final String MMS_SERVICE = "mms";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.content.ClipboardManager} for accessing and modifying
      * the contents of the global clipboard.
      *
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b85c58a..4bfc40e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2323,6 +2323,13 @@
     public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+     * has a telephony radio that support data.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TELEPHONY_DATA = "android.hardware.telephony.data";
+
+    /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
      * The device supports telephony carrier restriction mechanism.
      *
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index ac2e373..6639b3d 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -18,7 +18,7 @@
 
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
diff --git a/core/java/android/content/pm/parsing/ComponentParseUtils.java b/core/java/android/content/pm/parsing/ComponentParseUtils.java
index 5956857..f04a30c 100644
--- a/core/java/android/content/pm/parsing/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/ComponentParseUtils.java
@@ -27,8 +27,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityTaskManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index bba14c3..36ec67e 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -35,8 +35,8 @@
 import libcore.util.EmptyArray;
 
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -56,7 +56,7 @@
             "(?i)(AVG|COUNT|MAX|MIN|SUM|TOTAL|GROUP_CONCAT)\\((.+)\\)");
 
     private Map<String, String> mProjectionMap = null;
-    private List<Pattern> mProjectionGreylist = null;
+    private Collection<Pattern> mProjectionGreylist = null;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private String mTables = "";
@@ -196,20 +196,16 @@
      * Sets a projection greylist of columns that will be allowed through, even
      * when {@link #setStrict(boolean)} is enabled. This provides a way for
      * abusive custom columns like {@code COUNT(*)} to continue working.
-     *
-     * @hide
      */
-    public void setProjectionGreylist(@Nullable List<Pattern> projectionGreylist) {
+    public void setProjectionGreylist(@Nullable Collection<Pattern> projectionGreylist) {
         mProjectionGreylist = projectionGreylist;
     }
 
     /**
      * Gets the projection greylist for the query, as last configured by
-     * {@link #setProjectionGreylist(List)}.
-     *
-     * @hide
+     * {@link #setProjectionGreylist}.
      */
-    public @Nullable List<Pattern> getProjectionGreylist() {
+    public @Nullable Collection<Pattern> getProjectionGreylist() {
         return mProjectionGreylist;
     }
 
@@ -244,25 +240,27 @@
     }
 
     /**
-     * When set, the selection is verified against malicious arguments.
-     * When using this class to create a statement using
+     * When set, the selection is verified against malicious arguments. When
+     * using this class to create a statement using
      * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)},
-     * non-numeric limits will raise an exception. If a projection map is specified, fields
-     * not in that map will be ignored.
-     * If this class is used to execute the statement directly using
+     * non-numeric limits will raise an exception. If a projection map is
+     * specified, fields not in that map will be ignored. If this class is used
+     * to execute the statement directly using
      * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String)}
      * or
      * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String, String)},
-     * additionally also parenthesis escaping selection are caught.
-     *
-     * To summarize: To get maximum protection against malicious third party apps (for example
-     * content provider consumers), make sure to do the following:
+     * additionally also parenthesis escaping selection are caught. To
+     * summarize: To get maximum protection against malicious third party apps
+     * (for example content provider consumers), make sure to do the following:
      * <ul>
      * <li>Set this value to true</li>
      * <li>Use a projection map</li>
-     * <li>Use one of the query overloads instead of getting the statement as a sql string</li>
+     * <li>Use one of the query overloads instead of getting the statement as a
+     * sql string</li>
      * </ul>
-     * By default, this value is false.
+     * <p>
+     * This feature is disabled by default on each newly constructed
+     * {@link SQLiteQueryBuilder} and needs to be manually enabled.
      */
     public void setStrict(boolean strict) {
         if (strict) {
@@ -287,6 +285,9 @@
      * This enforcement applies to {@link #insert}, {@link #query}, and
      * {@link #update} operations. Any enforcement failures will throw an
      * {@link IllegalArgumentException}.
+     * <p>
+     * This feature is disabled by default on each newly constructed
+     * {@link SQLiteQueryBuilder} and needs to be manually enabled.
      */
     public void setStrictColumns(boolean strictColumns) {
         if (strictColumns) {
@@ -323,6 +324,9 @@
      * {@link #delete} operations. This enforcement does not apply to trusted
      * inputs, such as those provided by {@link #appendWhere}. Any enforcement
      * failures will throw an {@link IllegalArgumentException}.
+     * <p>
+     * This feature is disabled by default on each newly constructed
+     * {@link SQLiteQueryBuilder} and needs to be manually enabled.
      */
     public void setStrictGrammar(boolean strictGrammar) {
         if (strictGrammar) {
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 638d81b..6e1987c 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -90,4 +90,11 @@
 
     /** Create an input monitor for gestures. */
     InputMonitor monitorGestureInput(String name, int displayId);
+
+    // Add a runtime association between the input port and the display port. This overrides any
+    // static associations.
+    void addPortAssociation(in String inputPort, int displayPort);
+    // Remove the runtime association between the input port and the display port. Any existing
+    // static association for the cleared input port will be restored.
+    void removePortAssociation(in String inputPort);
 }
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 8d32db0..83f01a5 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -17,6 +17,7 @@
 package android.hardware.input;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemService;
@@ -963,6 +964,41 @@
         }
     }
 
+    /**
+     * Add a runtime association between the input port and the display port. This overrides any
+     * static associations.
+     * @param inputPort The port of the input device.
+     * @param displayPort The physical port of the associated display.
+     * <p>
+     * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT}.
+     * </p>
+     * @hide
+     */
+    public void addPortAssociation(@NonNull String inputPort, int displayPort) {
+        try {
+            mIm.addPortAssociation(inputPort, displayPort);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove the runtime association between the input port and the display port. Any existing
+     * static association for the cleared input port will be restored.
+     * @param inputPort The port of the input device to be cleared.
+     * <p>
+     * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT}.
+     * </p>
+     * @hide
+     */
+    public void removePortAssociation(@NonNull String inputPort) {
+        try {
+            mIm.removePortAssociation(inputPort);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     private void populateInputDevicesLocked() {
         if (mInputDevicesChangedListener == null) {
             final InputDevicesChangedListener listener = new InputDevicesChangedListener();
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 73b9d17..67fdda3 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -91,7 +91,7 @@
      *
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public static final String ACTION_USB_STATE =
             "android.hardware.usb.action.USB_STATE";
 
@@ -164,7 +164,7 @@
      *
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public static final String USB_CONNECTED = "connected";
 
     /**
@@ -181,6 +181,7 @@
      *
      * {@hide}
      */
+    @SystemApi
     public static final String USB_CONFIGURED = "configured";
 
     /**
@@ -217,6 +218,7 @@
      *
      * {@hide}
      */
+    @SystemApi
     public static final String USB_FUNCTION_RNDIS = "rndis";
 
     /**
@@ -319,6 +321,7 @@
      * Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
      * {@hide}
      */
+    @SystemApi
     public static final long FUNCTION_NONE = 0;
 
     /**
@@ -337,6 +340,7 @@
      * Code for the rndis usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
      * {@hide}
      */
+    @SystemApi
     public static final long FUNCTION_RNDIS = GadgetFunction.RNDIS;
 
     /**
@@ -698,6 +702,8 @@
      *
      * {@hide}
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
     public void setCurrentFunctions(long functions) {
         try {
             mService.setCurrentFunctions(functions);
@@ -737,6 +743,8 @@
      *
      * {@hide}
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
     public long getCurrentFunctions() {
         try {
             return mService.getCurrentFunctions();
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
new file mode 100644
index 0000000..6afdb5e
--- /dev/null
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -0,0 +1,242 @@
+/*
+ * 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 android.net;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.PersistableBundle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Class that provides utilities for collecting network connectivity diagnostics information.
+ * Connectivity information is made available through triggerable diagnostics tools and by listening
+ * to System validations. Some diagnostics information may be permissions-restricted.
+ *
+ * <p>ConnectivityDiagnosticsManager is intended for use by applications offering network
+ * connectivity on a user device. These tools will provide several mechanisms for these applications
+ * to be alerted to network conditions as well as diagnose potential network issues themselves.
+ *
+ * <p>The primary responsibilities of this class are to:
+ *
+ * <ul>
+ *   <li>Allow permissioned applications to register and unregister callbacks for network event
+ *       notifications
+ *   <li>Invoke callbacks for network event notifications, including:
+ *       <ul>
+ *         <li>Network validations
+ *         <li>Data stalls
+ *         <li>Connectivity reports from applications
+ *       </ul>
+ * </ul>
+ */
+public class ConnectivityDiagnosticsManager {
+    public static final int DETECTION_METHOD_DNS_EVENTS = 1;
+    public static final int DETECTION_METHOD_TCP_METRICS = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            prefix = {"DETECTION_METHOD_"},
+            value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS})
+    public @interface DetectionMethod {}
+
+    /** @hide */
+    public ConnectivityDiagnosticsManager() {}
+
+    /** Class that includes connectivity information for a specific Network at a specific time. */
+    public static class ConnectivityReport {
+        /** The Network for which this ConnectivityReport applied */
+        @NonNull public final Network network;
+
+        /**
+         * The timestamp for the report. The timestamp is taken from {@link
+         * System#currentTimeMillis}.
+         */
+        public final long reportTimestamp;
+
+        /** LinkProperties available on the Network at the reported timestamp */
+        @NonNull public final LinkProperties linkProperties;
+
+        /** NetworkCapabilities available on the Network at the reported timestamp */
+        @NonNull public final NetworkCapabilities networkCapabilities;
+
+        /** PersistableBundle that may contain additional info about the report */
+        @NonNull public final PersistableBundle additionalInfo;
+
+        /**
+         * Constructor for ConnectivityReport.
+         *
+         * <p>Apps should obtain instances through {@link
+         * ConnectivityDiagnosticsCallback#onConnectivityReport} instead of instantiating their own
+         * instances (unless for testing purposes).
+         *
+         * @param network The Network for which this ConnectivityReport applies
+         * @param reportTimestamp The timestamp for the report
+         * @param linkProperties The LinkProperties available on network at reportTimestamp
+         * @param networkCapabilities The NetworkCapabilities available on network at
+         *     reportTimestamp
+         * @param additionalInfo A PersistableBundle that may contain additional info about the
+         *     report
+         */
+        public ConnectivityReport(
+                @NonNull Network network,
+                long reportTimestamp,
+                @NonNull LinkProperties linkProperties,
+                @NonNull NetworkCapabilities networkCapabilities,
+                @NonNull PersistableBundle additionalInfo) {
+            this.network = network;
+            this.reportTimestamp = reportTimestamp;
+            this.linkProperties = linkProperties;
+            this.networkCapabilities = networkCapabilities;
+            this.additionalInfo = additionalInfo;
+        }
+    }
+
+    /** Class that includes information for a suspected data stall on a specific Network */
+    public static class DataStallReport {
+        /** The Network for which this DataStallReport applied */
+        @NonNull public final Network network;
+
+        /**
+         * The timestamp for the report. The timestamp is taken from {@link
+         * System#currentTimeMillis}.
+         */
+        public final long reportTimestamp;
+
+        /** The detection method used to identify the suspected data stall */
+        @DetectionMethod public final int detectionMethod;
+
+        /** PersistableBundle that may contain additional information on the suspected data stall */
+        @NonNull public final PersistableBundle stallDetails;
+
+        /**
+         * Constructor for DataStallReport.
+         *
+         * <p>Apps should obtain instances through {@link
+         * ConnectivityDiagnosticsCallback#onDataStallSuspected} instead of instantiating their own
+         * instances (unless for testing purposes).
+         *
+         * @param network The Network for which this DataStallReport applies
+         * @param reportTimestamp The timestamp for the report
+         * @param detectionMethod The detection method used to identify this data stall
+         * @param stallDetails A PersistableBundle that may contain additional info about the report
+         */
+        public DataStallReport(
+                @NonNull Network network,
+                long reportTimestamp,
+                @DetectionMethod int detectionMethod,
+                @NonNull PersistableBundle stallDetails) {
+            this.network = network;
+            this.reportTimestamp = reportTimestamp;
+            this.detectionMethod = detectionMethod;
+            this.stallDetails = stallDetails;
+        }
+    }
+
+    /**
+     * Abstract base class for Connectivity Diagnostics callbacks. Used for notifications about
+     * network connectivity events. Must be extended by applications wanting notifications.
+     */
+    public abstract static class ConnectivityDiagnosticsCallback {
+        /**
+         * Called when the platform completes a data connectivity check. This will also be invoked
+         * upon registration with the latest report.
+         *
+         * <p>The Network specified in the ConnectivityReport may not be active any more when this
+         * method is invoked.
+         *
+         * @param report The ConnectivityReport containing information about a connectivity check
+         */
+        public void onConnectivityReport(@NonNull ConnectivityReport report) {}
+
+        /**
+         * Called when the platform suspects a data stall on some Network.
+         *
+         * <p>The Network specified in the DataStallReport may not be active any more when this
+         * method is invoked.
+         *
+         * @param report The DataStallReport containing information about the suspected data stall
+         */
+        public void onDataStallSuspected(@NonNull DataStallReport report) {}
+
+        /**
+         * Called when any app reports connectivity to the System.
+         *
+         * @param network The Network for which connectivity has been reported
+         * @param hasConnectivity The connectivity reported to the System
+         */
+        public void onNetworkConnectivityReported(
+                @NonNull Network network, boolean hasConnectivity) {}
+    }
+
+    /**
+     * Registers a ConnectivityDiagnosticsCallback with the System.
+     *
+     * <p>Only apps that offer network connectivity to the user are allowed to register callbacks.
+     * This includes:
+     *
+     * <ul>
+     *   <li>Carrier apps with active subscriptions
+     *   <li>Active VPNs
+     *   <li>WiFi Suggesters
+     * </ul>
+     *
+     * <p>Callbacks will be limited to receiving notifications for networks over which apps provide
+     * connectivity.
+     *
+     * <p>If a registering app loses its relevant permissions, any callbacks it registered will
+     * silently stop receiving callbacks.
+     *
+     * <p>Each register() call <b>MUST</b> use a unique ConnectivityDiagnosticsCallback instance. If
+     * a single instance is registered with multiple NetworkRequests, an IllegalArgumentException
+     * will be thrown.
+     *
+     * @param request The NetworkRequest that will be used to match with Networks for which
+     *     callbacks will be fired
+     * @param e The Executor to be used for running the callback method invocations
+     * @param callback The ConnectivityDiagnosticsCallback that the caller wants registered with the
+     *     System
+     * @throws IllegalArgumentException if the same callback instance is registered with multiple
+     *     NetworkRequests
+     * @throws SecurityException if the caller does not have appropriate permissions to register a
+     *     callback
+     */
+    public void registerConnectivityDiagnosticsCallback(
+            @NonNull NetworkRequest request,
+            @NonNull Executor e,
+            @NonNull ConnectivityDiagnosticsCallback callback) {
+        // TODO(b/143187964): implement ConnectivityDiagnostics functionality
+        throw new UnsupportedOperationException("registerCallback() not supported yet");
+    }
+
+    /**
+     * Unregisters a ConnectivityDiagnosticsCallback with the System.
+     *
+     * <p>If the given callback is not currently registered with the System, this operation will be
+     * a no-op.
+     *
+     * @param callback The ConnectivityDiagnosticsCallback to be unregistered from the System.
+     */
+    public void unregisterConnectivityDiagnosticsCallback(
+            @NonNull ConnectivityDiagnosticsCallback callback) {
+        // TODO(b/143187964): implement ConnectivityDiagnostics functionality
+        throw new UnsupportedOperationException("registerCallback() not supported yet");
+    }
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 6da16a8..03d4200 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -363,7 +363,7 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     @UnsupportedAppUsage
     public static final String ACTION_TETHER_STATE_CHANGED =
-            "android.net.conn.TETHER_STATE_CHANGED";
+            TetheringManager.ACTION_TETHER_STATE_CHANGED;
 
     /**
      * @hide
@@ -371,14 +371,14 @@
      * tethering and currently available for tethering.
      */
     @UnsupportedAppUsage
-    public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+    public static final String EXTRA_AVAILABLE_TETHER = TetheringManager.EXTRA_AVAILABLE_TETHER;
 
     /**
      * @hide
      * gives a String[] listing all the interfaces currently in local-only
      * mode (ie, has DHCPv4+IPv6-ULA support and no packet forwarding)
      */
-    public static final String EXTRA_ACTIVE_LOCAL_ONLY = "localOnlyArray";
+    public static final String EXTRA_ACTIVE_LOCAL_ONLY = TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
 
     /**
      * @hide
@@ -386,7 +386,7 @@
      * (ie, has DHCPv4 support and packets potentially forwarded/NATed)
      */
     @UnsupportedAppUsage
-    public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+    public static final String EXTRA_ACTIVE_TETHER = TetheringManager.EXTRA_ACTIVE_TETHER;
 
     /**
      * @hide
@@ -395,7 +395,7 @@
      * for any interfaces listed here.
      */
     @UnsupportedAppUsage
-    public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+    public static final String EXTRA_ERRORED_TETHER = TetheringManager.EXTRA_ERRORED_TETHER;
 
     /**
      * Broadcast Action: The captive portal tracker has finished its test.
@@ -445,7 +445,7 @@
      * @see #startTethering(int, boolean, OnStartTetheringCallback)
      * @hide
      */
-    public static final int TETHERING_INVALID   = -1;
+    public static final int TETHERING_INVALID   = TetheringManager.TETHERING_INVALID;
 
     /**
      * Wifi tethering type.
@@ -453,7 +453,7 @@
      * @hide
      */
     @SystemApi
-    public static final int TETHERING_WIFI      = 0;
+    public static final int TETHERING_WIFI      = TetheringManager.TETHERING_WIFI;
 
     /**
      * USB tethering type.
@@ -461,7 +461,7 @@
      * @hide
      */
     @SystemApi
-    public static final int TETHERING_USB       = 1;
+    public static final int TETHERING_USB       = TetheringManager.TETHERING_USB;
 
     /**
      * Bluetooth tethering type.
@@ -469,7 +469,7 @@
      * @hide
      */
     @SystemApi
-    public static final int TETHERING_BLUETOOTH = 2;
+    public static final int TETHERING_BLUETOOTH = TetheringManager.TETHERING_BLUETOOTH;
 
     /**
      * Wifi P2p tethering type.
@@ -477,41 +477,41 @@
      * need to start from #startTethering(int, boolean, OnStartTetheringCallback).
      * @hide
      */
-    public static final int TETHERING_WIFI_P2P = 3;
+    public static final int TETHERING_WIFI_P2P = TetheringManager.TETHERING_WIFI_P2P;
 
     /**
      * Extra used for communicating with the TetherService. Includes the type of tethering to
      * enable if any.
      * @hide
      */
-    public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+    public static final String EXTRA_ADD_TETHER_TYPE = TetheringManager.EXTRA_ADD_TETHER_TYPE;
 
     /**
      * Extra used for communicating with the TetherService. Includes the type of tethering for
      * which to cancel provisioning.
      * @hide
      */
-    public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+    public static final String EXTRA_REM_TETHER_TYPE = TetheringManager.EXTRA_REM_TETHER_TYPE;
 
     /**
      * Extra used for communicating with the TetherService. True to schedule a recheck of tether
      * provisioning.
      * @hide
      */
-    public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+    public static final String EXTRA_SET_ALARM = TetheringManager.EXTRA_SET_ALARM;
 
     /**
      * Tells the TetherService to run a provision check now.
      * @hide
      */
-    public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+    public static final String EXTRA_RUN_PROVISION = TetheringManager.EXTRA_RUN_PROVISION;
 
     /**
      * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
      * which will receive provisioning results. Can be left empty.
      * @hide
      */
-    public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+    public static final String EXTRA_PROVISION_CALLBACK = TetheringManager.EXTRA_PROVISION_CALLBACK;
 
     /**
      * The absence of a connection type.
@@ -3107,6 +3107,63 @@
         }
     }
 
+    /**
+     * Registers the specified {@link NetworkProvider}.
+     * Each listener must only be registered once. The listener can be unregistered with
+     * {@link #unregisterNetworkProvider}.
+     *
+     * @param provider the provider to register
+     * @return the ID of the provider. This ID must be used by the provider when registering
+     *         {@link android.net.NetworkAgent}s.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    public int registerNetworkProvider(@NonNull NetworkProvider provider) {
+        if (provider.getProviderId() != NetworkProvider.ID_NONE) {
+            // TODO: Provide a better method for checking this by moving NetworkFactory.SerialNumber
+            // out of NetworkFactory.
+            throw new IllegalStateException("NetworkProviders can only be registered once");
+        }
+
+        try {
+            int providerId = mService.registerNetworkProvider(provider.getMessenger(),
+                    provider.getName());
+            provider.setProviderId(providerId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return provider.getProviderId();
+    }
+
+    /**
+     * Unregisters the specified NetworkProvider.
+     *
+     * @param provider the provider to unregister
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    public void unregisterNetworkProvider(@NonNull NetworkProvider provider) {
+        try {
+            mService.unregisterNetworkProvider(provider.getMessenger());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        provider.setProviderId(NetworkProvider.ID_NONE);
+    }
+
+
+    /** @hide exposed via the NetworkProvider class. */
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) {
+        try {
+            mService.declareNetworkRequestUnfulfillable(request);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     // TODO : remove this method. It is a stopgap measure to help sheperding a number
     // of dependent changes that would conflict throughout the automerger graph. Having this
     // temporarily helps with the process of going through with all these dependent changes across
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 09c02ef..e6a0379 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -142,12 +142,16 @@
 
     void setAirplaneMode(boolean enable);
 
-    int registerNetworkFactory(in Messenger messenger, in String name);
-
     boolean requestBandwidthUpdate(in Network network);
 
+    int registerNetworkFactory(in Messenger messenger, in String name);
     void unregisterNetworkFactory(in Messenger messenger);
 
+    int registerNetworkProvider(in Messenger messenger, in String name);
+    void unregisterNetworkProvider(in Messenger messenger);
+
+    void declareNetworkRequestUnfulfillable(in NetworkRequest request);
+
     int registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
             in NetworkCapabilities nc, int score, in NetworkMisc misc, in int factorySerialNumber);
 
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 45d0c73..09ec6c3 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -17,7 +17,6 @@
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -26,6 +25,7 @@
 import android.annotation.TestApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.net.annotations.PolicyDirection;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -41,8 +41,6 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.net.DatagramSocket;
 import java.net.InetAddress;
 import java.net.Socket;
@@ -78,11 +76,6 @@
      */
     public static final int DIRECTION_OUT = 1;
 
-    /** @hide */
-    @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface PolicyDirection {}
-
     /**
      * The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
      *
diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java
new file mode 100644
index 0000000..2c0e4aa
--- /dev/null
+++ b/core/java/android/net/NetworkProvider.java
@@ -0,0 +1,166 @@
+/*
+ * 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.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.util.Log;
+
+/**
+ * Base class for network providers such as telephony or Wi-Fi. NetworkProviders connect the device
+ * to networks and makes them available to to the core network stack by creating
+ * {@link NetworkAgent}s. The networks can then provide connectivity to apps and can be interacted
+ * with via networking APIs such as {@link ConnectivityManager}.
+ *
+ * Subclasses should implement {@link #onNetworkRequested} and {@link #onRequestWithdrawn} to
+ * receive {@link NetworkRequest}s sent by the system and by apps. A network that is not the
+ * best (highest-scoring) network for any request is generally not used by the system, and torn
+ * down.
+ *
+ * @hide
+ */
+@SystemApi
+public class NetworkProvider {
+    /**
+     * {@code providerId} value that indicates the absence of a provider. It is the providerId of
+     * any NetworkProvider that is not currently registered, and of any NetworkRequest that is not
+     * currently being satisfied by a network.
+     */
+    public static final int ID_NONE = -1;
+
+    /**
+     * A hardcoded ID for NetworkAgents representing VPNs. These agents are not created by any
+     * provider, so they use this constant for clarity instead of NONE.
+     * @hide only used by ConnectivityService.
+     */
+    public static final int ID_VPN = -2;
+
+    /**
+     * The first providerId value that will be allocated.
+     * @hide only used by ConnectivityService.
+     */
+    public static final int FIRST_PROVIDER_ID = 1;
+
+    /** @hide only used by ConnectivityService */
+    public static final int CMD_REQUEST_NETWORK = 1;
+    /** @hide only used by ConnectivityService */
+    public static final int CMD_CANCEL_REQUEST = 2;
+
+    private final Messenger mMessenger;
+    private final String mName;
+    private final ConnectivityManager mCm;
+
+    private int mProviderId = ID_NONE;
+
+    /**
+     * Constructs a new NetworkProvider.
+     *
+     * @param looper the Looper on which to run {@link #onNetworkRequested} and
+     *               {@link #onRequestWithdrawn}.
+     * @param name the name of the listener, used only for debugging.
+     *
+     * @hide
+     */
+    @SystemApi
+    public NetworkProvider(@NonNull Context context, @NonNull Looper looper, @NonNull String name) {
+        mCm = ConnectivityManager.from(context);
+
+        Handler handler = new Handler(looper) {
+            @Override
+            public void handleMessage(Message m) {
+                switch (m.what) {
+                    case CMD_REQUEST_NETWORK:
+                        onNetworkRequested((NetworkRequest) m.obj, m.arg1, m.arg2);
+                        break;
+                    case CMD_CANCEL_REQUEST:
+                        onRequestWithdrawn((NetworkRequest) m.obj);
+                        break;
+                    default:
+                        Log.e(mName, "Unhandled message: " + m.what);
+                }
+            }
+        };
+        mMessenger = new Messenger(handler);
+        mName = name;
+    }
+
+    // TODO: consider adding a register() method so ConnectivityManager does not need to call this.
+    public @Nullable Messenger getMessenger() {
+        return mMessenger;
+    }
+
+    public @NonNull String getName() {
+        return mName;
+    }
+
+    /**
+     * Returns the ID of this provider. This is known only once the provider is registered via
+     * {@link ConnectivityManager#registerNetworkProvider()}, otherwise the ID is {@link #ID_NONE}.
+     * This ID must be used when registering any {@link NetworkAgent}s.
+     */
+    public int getProviderId() {
+        return mProviderId;
+    }
+
+    /** @hide */
+    public void setProviderId(int providerId) {
+        mProviderId = providerId;
+    }
+
+    /**
+     *  Called when a NetworkRequest is received. The request may be a new request or an existing
+     *  request with a different score.
+     *
+     * @param request the NetworkRequest being received
+     * @param score the score of the network currently satisfying the request, or 0 if none.
+     * @param providerId the ID of the provider that created the network currently satisfying this
+     *                   request, or {@link #ID_NONE} if none.
+     *
+     *  @hide
+     */
+    @SystemApi
+    public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {}
+
+    /**
+     *  Called when a NetworkRequest is withdrawn.
+     *  @hide
+     */
+    @SystemApi
+    public void onRequestWithdrawn(@NonNull NetworkRequest request) {}
+
+    /**
+     * Asserts that no provider will ever be able to satisfy the specified request. The provider
+     * must only call this method if it knows that it is the only provider on the system capable of
+     * satisfying this request, and that the request cannot be satisfied. The application filing the
+     * request will receive an {@link NetworkCallback#onUnavailable()} callback.
+     *
+     * @param request the request that cannot be fulfilled
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) {
+        mCm.declareNetworkRequestUnfulfillable(request);
+    }
+}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 45bf4d2..96d7a80 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -19,6 +19,9 @@
 import static android.os.Process.CLAT_UID;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -48,59 +51,104 @@
  * @hide
  */
 // @NotThreadSafe
-public class NetworkStats implements Parcelable {
+@SystemApi
+public final class NetworkStats implements Parcelable {
     private static final String TAG = "NetworkStats";
+
     /** {@link #iface} value when interface details unavailable. */
-    public static final String IFACE_ALL = null;
+    @SuppressLint("CompileTimeConstant")
+    @Nullable public static final String IFACE_ALL = null;
+    /**
+     * Virtual network interface for video telephony. This is for VT data usage counting
+     * purpose.
+     */
+    public static final String IFACE_VT = "vt_data0";
+
     /** {@link #uid} value when UID details unavailable. */
     public static final int UID_ALL = -1;
-    /** {@link #tag} value matching any tag. */
+    /** Special UID value for data usage by tethering. */
+    public static final int UID_TETHERING = -5;
+
+    /**
+     * {@link #tag} value matching any tag.
+     * @hide
+     */
     // TODO: Rename TAG_ALL to TAG_ANY.
     public static final int TAG_ALL = -1;
-    /** {@link #set} value for all sets combined, not including debug sets. */
+    /**
+     * {@link #set} value for all sets combined, not including debug sets.
+     * @hide
+     */
     public static final int SET_ALL = -1;
     /** {@link #set} value where background data is accounted. */
     public static final int SET_DEFAULT = 0;
     /** {@link #set} value where foreground data is accounted. */
     public static final int SET_FOREGROUND = 1;
-    /** All {@link #set} value greater than SET_DEBUG_START are debug {@link #set} values. */
+    /**
+     * All {@link #set} value greater than SET_DEBUG_START are debug {@link #set} values.
+     * @hide
+     */
     public static final int SET_DEBUG_START = 1000;
-    /** Debug {@link #set} value when the VPN stats are moved in. */
+    /**
+     * Debug {@link #set} value when the VPN stats are moved in.
+     * @hide
+     */
     public static final int SET_DBG_VPN_IN = 1001;
-    /** Debug {@link #set} value when the VPN stats are moved out of a vpn UID. */
+    /**
+     * Debug {@link #set} value when the VPN stats are moved out of a vpn UID.
+     * @hide
+     */
     public static final int SET_DBG_VPN_OUT = 1002;
 
-    /** Include all interfaces when filtering */
-    public static final String[] INTERFACES_ALL = null;
+    /**
+     * Include all interfaces when filtering
+     * @hide
+     */
+    public @Nullable static final String[] INTERFACES_ALL = null;
 
     /** {@link #tag} value for total data across all tags. */
     // TODO: Rename TAG_NONE to TAG_ALL.
     public static final int TAG_NONE = 0;
 
-    /** {@link #metered} value to account for all metered states. */
+    /**
+     * {@link #metered} value to account for all metered states.
+     * @hide
+     */
     public static final int METERED_ALL = -1;
     /** {@link #metered} value where native, unmetered data is accounted. */
     public static final int METERED_NO = 0;
     /** {@link #metered} value where metered data is accounted. */
     public static final int METERED_YES = 1;
 
-    /** {@link #roaming} value to account for all roaming states. */
+    /**
+     * {@link #roaming} value to account for all roaming states.
+     * @hide
+     */
     public static final int ROAMING_ALL = -1;
     /** {@link #roaming} value where native, non-roaming data is accounted. */
     public static final int ROAMING_NO = 0;
     /** {@link #roaming} value where roaming data is accounted. */
     public static final int ROAMING_YES = 1;
 
-    /** {@link #onDefaultNetwork} value to account for all default network states. */
+    /**
+     * {@link #onDefaultNetwork} value to account for all default network states.
+     * @hide
+     */
     public static final int DEFAULT_NETWORK_ALL = -1;
     /** {@link #onDefaultNetwork} value to account for usage while not the default network. */
     public static final int DEFAULT_NETWORK_NO = 0;
     /** {@link #onDefaultNetwork} value to account for usage while the default network. */
     public static final int DEFAULT_NETWORK_YES = 1;
 
-    /** Denotes a request for stats at the interface level. */
+    /**
+     * Denotes a request for stats at the interface level.
+     * @hide
+     */
     public static final int STATS_PER_IFACE = 0;
-    /** Denotes a request for stats at the interface and UID level. */
+    /**
+     * Denotes a request for stats at the interface and UID level.
+     * @hide
+     */
     public static final int STATS_PER_UID = 1;
 
     private static final String CLATD_INTERFACE_PREFIX = "v4-";
@@ -144,60 +192,78 @@
     @UnsupportedAppUsage
     private long[] operations;
 
+    /** @hide */
+    @SystemApi
     public static class Entry {
+        /** @hide */
         @UnsupportedAppUsage
         public String iface;
+        /** @hide */
         @UnsupportedAppUsage
         public int uid;
+        /** @hide */
         @UnsupportedAppUsage
         public int set;
+        /** @hide */
         @UnsupportedAppUsage
         public int tag;
         /**
          * Note that this is only populated w/ the default value when read from /proc or written
          * to disk. We merge in the correct value when reporting this value to clients of
          * getSummary().
+         * @hide
          */
         public int metered;
         /**
          * Note that this is only populated w/ the default value when read from /proc or written
          * to disk. We merge in the correct value when reporting this value to clients of
          * getSummary().
+         * @hide
          */
         public int roaming;
         /**
          * Note that this is only populated w/ the default value when read from /proc or written
          * to disk. We merge in the correct value when reporting this value to clients of
          * getSummary().
+         * @hide
          */
         public int defaultNetwork;
+        /** @hide */
         @UnsupportedAppUsage
         public long rxBytes;
+        /** @hide */
         @UnsupportedAppUsage
         public long rxPackets;
+        /** @hide */
         @UnsupportedAppUsage
         public long txBytes;
+        /** @hide */
         @UnsupportedAppUsage
         public long txPackets;
+        /** @hide */
+        @UnsupportedAppUsage
         public long operations;
 
+        /** @hide */
         @UnsupportedAppUsage
         public Entry() {
             this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
         }
 
+        /** @hide */
         public Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
             this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets,
                     operations);
         }
 
+        /** @hide */
         public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets,
                 long txBytes, long txPackets, long operations) {
             this(iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
                     rxBytes, rxPackets, txBytes, txPackets, operations);
         }
 
-        public Entry(String iface, int uid, int set, int tag, int metered, int roaming,
+        public Entry(@Nullable String iface, int uid, int set, int tag, int metered, int roaming,
                  int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
                  long operations) {
             this.iface = iface;
@@ -214,15 +280,18 @@
             this.operations = operations;
         }
 
+        /** @hide */
         public boolean isNegative() {
             return rxBytes < 0 || rxPackets < 0 || txBytes < 0 || txPackets < 0 || operations < 0;
         }
 
+        /** @hide */
         public boolean isEmpty() {
             return rxBytes == 0 && rxPackets == 0 && txBytes == 0 && txPackets == 0
                     && operations == 0;
         }
 
+        /** @hide */
         public void add(Entry another) {
             this.rxBytes += another.rxBytes;
             this.rxPackets += another.rxPackets;
@@ -249,6 +318,7 @@
             return builder.toString();
         }
 
+        /** @hide */
         @Override
         public boolean equals(Object o) {
             if (o instanceof Entry) {
@@ -262,13 +332,13 @@
             return false;
         }
 
+        /** @hide */
         @Override
         public int hashCode() {
             return Objects.hash(uid, set, tag, metered, roaming, defaultNetwork, iface);
         }
     }
 
-    @UnsupportedAppUsage
     public NetworkStats(long elapsedRealtime, int initialSize) {
         this.elapsedRealtime = elapsedRealtime;
         this.size = 0;
@@ -292,6 +362,7 @@
         }
     }
 
+    /** @hide */
     @UnsupportedAppUsage
     public NetworkStats(Parcel parcel) {
         elapsedRealtime = parcel.readLong();
@@ -312,7 +383,7 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeLong(elapsedRealtime);
         dest.writeInt(size);
         dest.writeInt(capacity);
@@ -330,19 +401,23 @@
         dest.writeLongArray(operations);
     }
 
+    /**
+     * @hide
+     */
     @Override
     public NetworkStats clone() {
         final NetworkStats clone = new NetworkStats(elapsedRealtime, size);
         NetworkStats.Entry entry = null;
         for (int i = 0; i < size; i++) {
             entry = getValues(i, entry);
-            clone.addValues(entry);
+            clone.addEntry(entry);
         }
         return clone;
     }
 
     /**
      * Clear all data stored in this object.
+     * @hide
      */
     public void clear() {
         this.capacity = 0;
@@ -360,25 +435,28 @@
         this.operations = EmptyArray.LONG;
     }
 
+    /** @hide */
     @VisibleForTesting
     public NetworkStats addIfaceValues(
             String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
-        return addValues(
+        return addEntry(
                 iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L);
     }
 
+    /** @hide */
     @VisibleForTesting
-    public NetworkStats addValues(String iface, int uid, int set, int tag, long rxBytes,
+    public NetworkStats addEntry(String iface, int uid, int set, int tag, long rxBytes,
             long rxPackets, long txBytes, long txPackets, long operations) {
-        return addValues(new Entry(
+        return addEntry(new Entry(
                 iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
     }
 
+    /** @hide */
     @VisibleForTesting
-    public NetworkStats addValues(String iface, int uid, int set, int tag, int metered, int roaming,
+    public NetworkStats addEntry(String iface, int uid, int set, int tag, int metered, int roaming,
             int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
             long operations) {
-        return addValues(new Entry(
+        return addEntry(new Entry(
                 iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets,
                 txBytes, txPackets, operations));
     }
@@ -386,8 +464,9 @@
     /**
      * Add new stats entry, copying from given {@link Entry}. The {@link Entry}
      * object can be recycled across multiple calls.
+     * @hide
      */
-    public NetworkStats addValues(Entry entry) {
+    public NetworkStats addEntry(Entry entry) {
         if (size >= capacity) {
             final int newLength = Math.max(size, 10) * 3 / 2;
             iface = Arrays.copyOf(iface, newLength);
@@ -428,6 +507,7 @@
 
     /**
      * Return specific stats entry.
+     * @hide
      */
     @UnsupportedAppUsage
     public Entry getValues(int i, Entry recycle) {
@@ -467,10 +547,12 @@
         operations[dest] = operations[src];
     }
 
+    /** @hide */
     public long getElapsedRealtime() {
         return elapsedRealtime;
     }
 
+    /** @hide */
     public void setElapsedRealtime(long time) {
         elapsedRealtime = time;
     }
@@ -478,21 +560,25 @@
     /**
      * Return age of this {@link NetworkStats} object with respect to
      * {@link SystemClock#elapsedRealtime()}.
+     * @hide
      */
     public long getElapsedRealtimeAge() {
         return SystemClock.elapsedRealtime() - elapsedRealtime;
     }
 
+    /** @hide */
     @UnsupportedAppUsage
     public int size() {
         return size;
     }
 
+    /** @hide */
     @VisibleForTesting
     public int internalSize() {
         return capacity;
     }
 
+    /** @hide */
     @Deprecated
     public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
             long txBytes, long txPackets, long operations) {
@@ -501,6 +587,7 @@
                 txPackets, operations);
     }
 
+    /** @hide */
     public NetworkStats combineValues(String iface, int uid, int set, int tag,
             long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
         return combineValues(new Entry(
@@ -509,16 +596,20 @@
 
     /**
      * Combine given values with an existing row, or create a new row if
-     * {@link #findIndex(String, int, int, int, int)} is unable to find match. Can
-     * also be used to subtract values from existing rows.
+     * {@link #findIndex(String, int, int, int, int, int, int)} is unable to find match. Can
+     * also be used to subtract values from existing rows. This method mutates the referencing
+     * {@link NetworkStats} object.
+     *
+     * @param entry the {@link Entry} to combine.
+     * @return a reference to this mutated {@link NetworkStats} object.
+     * @hide
      */
-    @UnsupportedAppUsage
-    public NetworkStats combineValues(Entry entry) {
+    public @NonNull NetworkStats combineValues(@NonNull Entry entry) {
         final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag, entry.metered,
                 entry.roaming, entry.defaultNetwork);
         if (i == -1) {
             // only create new entry when positive contribution
-            addValues(entry);
+            addEntry(entry);
         } else {
             rxBytes[i] += entry.rxBytes;
             rxPackets[i] += entry.rxPackets;
@@ -530,10 +621,33 @@
     }
 
     /**
-     * Combine all values from another {@link NetworkStats} into this object.
+     * Add given values with an existing row, or create a new row if
+     * {@link #findIndex(String, int, int, int, int, int, int)} is unable to find match. Can
+     * also be used to subtract values from existing rows.
+     *
+     * @param entry the {@link Entry} to add.
+     * @return a new constructed {@link NetworkStats} object that contains the result.
      */
-    @UnsupportedAppUsage
-    public void combineAllValues(NetworkStats another) {
+    public @NonNull NetworkStats addValues(@NonNull Entry entry) {
+        return this.clone().combineValues(entry);
+    }
+
+    /**
+     * Add the given {@link NetworkStats} objects.
+     *
+     * @return the sum of two objects.
+     */
+    public @NonNull NetworkStats add(@NonNull NetworkStats another) {
+        final NetworkStats ret = this.clone();
+        ret.combineAllValues(another);
+        return ret;
+    }
+
+    /**
+     * Combine all values from another {@link NetworkStats} into this object.
+     * @hide
+     */
+    public void combineAllValues(@NonNull NetworkStats another) {
         NetworkStats.Entry entry = null;
         for (int i = 0; i < another.size; i++) {
             entry = another.getValues(i, entry);
@@ -543,6 +657,7 @@
 
     /**
      * Find first stats index that matches the requested parameters.
+     * @hide
      */
     public int findIndex(String iface, int uid, int set, int tag, int metered, int roaming,
             int defaultNetwork) {
@@ -560,6 +675,7 @@
     /**
      * Find first stats index that matches the requested parameters, starting
      * search around the hinted index as an optimization.
+     * @hide
      */
     @VisibleForTesting
     public int findIndexHinted(String iface, int uid, int set, int tag, int metered, int roaming,
@@ -589,6 +705,7 @@
      * Splice in {@link #operations} from the given {@link NetworkStats} based
      * on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface},
      * since operation counts are at data layer.
+     * @hide
      */
     public void spliceOperationsFrom(NetworkStats stats) {
         for (int i = 0; i < size; i++) {
@@ -604,6 +721,7 @@
 
     /**
      * Return list of unique interfaces known by this data structure.
+     * @hide
      */
     public String[] getUniqueIfaces() {
         final HashSet<String> ifaces = new HashSet<String>();
@@ -617,6 +735,7 @@
 
     /**
      * Return list of unique UIDs known by this data structure.
+     * @hide
      */
     @UnsupportedAppUsage
     public int[] getUniqueUids() {
@@ -636,6 +755,7 @@
     /**
      * Return total bytes represented by this snapshot object, usually used when
      * checking if a {@link #subtract(NetworkStats)} delta passes a threshold.
+     * @hide
      */
     @UnsupportedAppUsage
     public long getTotalBytes() {
@@ -645,6 +765,7 @@
 
     /**
      * Return total of all fields represented by this snapshot object.
+     * @hide
      */
     @UnsupportedAppUsage
     public Entry getTotal(Entry recycle) {
@@ -654,6 +775,7 @@
     /**
      * Return total of all fields represented by this snapshot object matching
      * the requested {@link #uid}.
+     * @hide
      */
     @UnsupportedAppUsage
     public Entry getTotal(Entry recycle, int limitUid) {
@@ -663,11 +785,13 @@
     /**
      * Return total of all fields represented by this snapshot object matching
      * the requested {@link #iface}.
+     * @hide
      */
     public Entry getTotal(Entry recycle, HashSet<String> limitIface) {
         return getTotal(recycle, limitIface, UID_ALL, false);
     }
 
+    /** @hide */
     @UnsupportedAppUsage
     public Entry getTotalIncludingTags(Entry recycle) {
         return getTotal(recycle, null, UID_ALL, true);
@@ -717,6 +841,7 @@
 
     /**
      * Fast path for battery stats.
+     * @hide
      */
     public long getTotalPackets() {
         long total = 0;
@@ -729,9 +854,12 @@
     /**
      * Subtract the given {@link NetworkStats}, effectively leaving the delta
      * between two snapshots in time. Assumes that statistics rows collect over
-     * time, and that none of them have disappeared.
+     * time, and that none of them have disappeared. This method does not mutate
+     * the referencing object.
+     *
+     * @return the delta between two objects.
      */
-    public NetworkStats subtract(NetworkStats right) {
+    public @NonNull NetworkStats subtract(@NonNull NetworkStats right) {
         return subtract(this, right, null, null);
     }
 
@@ -742,6 +870,7 @@
      * <p>
      * If counters have rolled backwards, they are clamped to {@code 0} and
      * reported to the given {@link NonMonotonicObserver}.
+     * @hide
      */
     public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right,
             NonMonotonicObserver<C> observer, C cookie) {
@@ -759,6 +888,7 @@
      * If <var>recycle</var> is supplied, this NetworkStats object will be
      * reused (and returned) as the result if it is large enough to contain
      * the data.
+     * @hide
      */
     public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right,
             NonMonotonicObserver<C> observer, C cookie, NetworkStats recycle) {
@@ -817,7 +947,7 @@
                 entry.operations = Math.max(entry.operations, 0);
             }
 
-            result.addValues(entry);
+            result.addEntry(entry);
         }
 
         return result;
@@ -847,6 +977,7 @@
      * @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated.
      * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
      * @param useBpfStats True if eBPF is in use.
+     * @hide
      */
     public static void apply464xlatAdjustments(NetworkStats baseTraffic,
             NetworkStats stackedTraffic, Map<String, String> stackedIfaces, boolean useBpfStats) {
@@ -899,6 +1030,7 @@
      * {@link #apply464xlatAdjustments(NetworkStats, NetworkStats, Map)} with {@code this} as
      * base and stacked traffic.
      * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
+     * @hide
      */
     public void apply464xlatAdjustments(Map<String, String> stackedIfaces, boolean useBpfStats) {
         apply464xlatAdjustments(this, this, stackedIfaces, useBpfStats);
@@ -907,6 +1039,7 @@
     /**
      * Return total statistics grouped by {@link #iface}; doesn't mutate the
      * original structure.
+     * @hide
      */
     public NetworkStats groupedByIface() {
         final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
@@ -938,6 +1071,7 @@
     /**
      * Return total statistics grouped by {@link #uid}; doesn't mutate the
      * original structure.
+     * @hide
      */
     public NetworkStats groupedByUid() {
         final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
@@ -968,6 +1102,7 @@
 
     /**
      * Remove all rows that match one of specified UIDs.
+     * @hide
      */
     public void removeUids(int[] uids) {
         int nextOutputEntry = 0;
@@ -989,6 +1124,7 @@
      * @param limitUid UID to filter for, or {@link #UID_ALL}.
      * @param limitIfaces Interfaces to filter for, or {@link #INTERFACES_ALL}.
      * @param limitTag Tag to filter for, or {@link #TAG_ALL}.
+     * @hide
      */
     public void filter(int limitUid, String[] limitIfaces, int limitTag) {
         if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) {
@@ -1004,6 +1140,7 @@
      * Only keep entries with {@link #set} value less than {@link #SET_DEBUG_START}.
      *
      * <p>This mutates the original structure in place.
+     * @hide
      */
     public void filterDebugEntries() {
         filter(e -> e.set < SET_DEBUG_START);
@@ -1024,6 +1161,7 @@
         size = nextOutputEntry;
     }
 
+    /** @hide */
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix);
         pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
@@ -1047,6 +1185,7 @@
 
     /**
      * Return text description of {@link #set} value.
+     * @hide
      */
     public static String setToString(int set) {
         switch (set) {
@@ -1067,6 +1206,7 @@
 
     /**
      * Return text description of {@link #set} value.
+     * @hide
      */
     public static String setToCheckinString(int set) {
         switch (set) {
@@ -1087,6 +1227,7 @@
 
     /**
      * @return true if the querySet matches the dataSet.
+     * @hide
      */
     public static boolean setMatches(int querySet, int dataSet) {
         if (querySet == dataSet) {
@@ -1098,6 +1239,7 @@
 
     /**
      * Return text description of {@link #tag} value.
+     * @hide
      */
     public static String tagToString(int tag) {
         return "0x" + Integer.toHexString(tag);
@@ -1105,6 +1247,7 @@
 
     /**
      * Return text description of {@link #metered} value.
+     * @hide
      */
     public static String meteredToString(int metered) {
         switch (metered) {
@@ -1121,6 +1264,7 @@
 
     /**
      * Return text description of {@link #roaming} value.
+     * @hide
      */
     public static String roamingToString(int roaming) {
         switch (roaming) {
@@ -1137,6 +1281,7 @@
 
     /**
      * Return text description of {@link #defaultNetwork} value.
+     * @hide
      */
     public static String defaultNetworkToString(int defaultNetwork) {
         switch (defaultNetwork) {
@@ -1151,6 +1296,7 @@
         }
     }
 
+    /** @hide */
     @Override
     public String toString() {
         final CharArrayWriter writer = new CharArrayWriter();
@@ -1163,8 +1309,7 @@
         return 0;
     }
 
-    @UnsupportedAppUsage
-    public static final @android.annotation.NonNull Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
+    public static final @NonNull Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
         @Override
         public NetworkStats createFromParcel(Parcel in) {
             return new NetworkStats(in);
@@ -1176,6 +1321,7 @@
         }
     };
 
+    /** @hide */
     public interface NonMonotonicObserver<C> {
         public void foundNonMonotonic(
                 NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie);
@@ -1195,6 +1341,7 @@
      * @param tunUid uid of the VPN application
      * @param tunIface iface of the vpn tunnel
      * @param underlyingIfaces underlying network ifaces used by the VPN application
+     * @hide
      */
     public void migrateTun(int tunUid, @NonNull String tunIface,
             @NonNull String[] underlyingIfaces) {
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 162d6e2..8108cf0 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -89,7 +89,7 @@
      *
      * @hide
      */
-    public static final int UID_TETHERING = -5;
+    public static final int UID_TETHERING = NetworkStats.UID_TETHERING;
 
     /**
      * Tag values in this range are reserved for the network stack. The network stack is
diff --git a/core/java/android/net/annotations/PolicyDirection.java b/core/java/android/net/annotations/PolicyDirection.java
new file mode 100644
index 0000000..febd9b4
--- /dev/null
+++ b/core/java/android/net/annotations/PolicyDirection.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 android.net.annotations;
+
+import android.annotation.IntDef;
+import android.net.IpSecManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * IPsec traffic direction.
+ *
+ * <p>Mainline modules cannot reference hidden @IntDef. Moving this annotation to a separate class
+ * to allow others to statically include it.
+ *
+ * @hide
+ */
+@IntDef(value = {IpSecManager.DIRECTION_IN, IpSecManager.DIRECTION_OUT})
+@Retention(RetentionPolicy.SOURCE)
+public @interface PolicyDirection {}
diff --git a/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java b/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java
new file mode 100644
index 0000000..740aa92
--- /dev/null
+++ b/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java
@@ -0,0 +1,70 @@
+/*
+ * 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.net.netstats.provider;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.NetworkStats;
+
+/**
+ * A base class that allows external modules to implement a custom network statistics provider.
+ * @hide
+ */
+@SystemApi
+public abstract class AbstractNetworkStatsProvider {
+    /**
+     * A value used by {@link #setLimit} and {@link #setAlert} indicates there is no limit.
+     */
+    public static final int QUOTA_UNLIMITED = -1;
+
+    /**
+     * Called by {@code NetworkStatsService} when global polling is needed. Custom
+     * implementation of providers MUST respond to it by calling
+     * {@link NetworkStatsProviderCallback#onStatsUpdated} within one minute. Responding
+     * later than this may cause the stats to be dropped.
+     *
+     * @param token a positive number identifying the new state of the system under which
+     *              {@link NetworkStats} have to be gathered from now on. When this is called,
+     *              custom implementations of providers MUST report the latest stats with the
+     *              previous token, under which stats were being gathered so far.
+     */
+    public abstract void requestStatsUpdate(int token);
+
+    /**
+     * Called by {@code NetworkStatsService} when setting the interface quota for the specified
+     * upstream interface. When this is called, the custom implementation should block all egress
+     * packets on the {@code iface} associated with the provider when {@code quotaBytes} bytes have
+     * been reached, and MUST respond to it by calling
+     * {@link NetworkStatsProviderCallback#onLimitReached()}.
+     *
+     * @param iface the interface requiring the operation.
+     * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
+     *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
+     */
+    public abstract void setLimit(@NonNull String iface, long quotaBytes);
+
+    /**
+     * Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations
+     * MUST call {@link NetworkStatsProviderCallback#onAlertReached()} when {@code quotaBytes} bytes
+     * have been reached. Unlike {@link #setLimit(String, long)}, the custom implementation should
+     * not block all egress packets.
+     *
+     * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
+     *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no alert.
+     */
+    public abstract void setAlert(long quotaBytes);
+}
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
new file mode 100644
index 0000000..55b3d4e
--- /dev/null
+++ b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.net.netstats.provider;
+
+/**
+ * Interface for NetworkStatsService to query network statistics and set data limits.
+ *
+ * @hide
+ */
+oneway interface INetworkStatsProvider {
+    void requestStatsUpdate(int token);
+    void setLimit(String iface, long quotaBytes);
+    void setAlert(long quotaBytes);
+}
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
new file mode 100644
index 0000000..3ea9318
--- /dev/null
+++ b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.net.netstats.provider;
+
+import android.net.NetworkStats;
+
+/**
+ * Interface for implementor of {@link INetworkStatsProviderCallback} to push events
+ * such as network statistics update or notify limit reached.
+ * @hide
+ */
+oneway interface INetworkStatsProviderCallback {
+    void onStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats);
+    void onAlertReached();
+    void onLimitReached();
+    void unregister();
+}
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java b/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java
new file mode 100644
index 0000000..e17a8ee
--- /dev/null
+++ b/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java
@@ -0,0 +1,98 @@
+/*
+ * 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.net.netstats.provider;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.net.NetworkStats;
+import android.os.RemoteException;
+
+/**
+ * A callback class that allows callers to report events to the system.
+ * @hide
+ */
+@SystemApi
+@SuppressLint("CallbackMethodName")
+public class NetworkStatsProviderCallback {
+    @NonNull private final INetworkStatsProviderCallback mBinder;
+
+    /** @hide */
+    public NetworkStatsProviderCallback(@NonNull INetworkStatsProviderCallback binder) {
+        mBinder = binder;
+    }
+
+    /**
+     * Notify the system of new network statistics.
+     *
+     * Send the network statistics recorded since the last call to {@link #onStatsUpdated}. Must be
+     * called within one minute of {@link AbstractNetworkStatsProvider#requestStatsUpdate(int)}
+     * being called. The provider can also call this whenever it wants to reports new stats for any
+     * reason. Note that the system will not necessarily immediately propagate the statistics to
+     * reflect the update.
+     *
+     * @param token the token under which these stats were gathered. Providers can call this method
+     *              with the current token as often as they want, until the token changes.
+     *              {@see AbstractNetworkStatsProvider#requestStatsUpdate()}
+     * @param ifaceStats the {@link NetworkStats} per interface to be reported.
+     *                   The provider should not include any traffic that is already counted by
+     *                   kernel interface counters.
+     * @param uidStats the same stats as above, but counts {@link NetworkStats}
+     *                 per uid.
+     */
+    public void onStatsUpdated(int token, @NonNull NetworkStats ifaceStats,
+            @NonNull NetworkStats uidStats) {
+        try {
+            mBinder.onStatsUpdated(token, ifaceStats, uidStats);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Notify system that the quota set by {@code setAlert} has been reached.
+     */
+    public void onAlertReached() {
+        try {
+            mBinder.onAlertReached();
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Notify system that the quota set by {@code setLimit} has been reached.
+     */
+    public void onLimitReached() {
+        try {
+            mBinder.onLimitReached();
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Unregister the provider and the referencing callback.
+     */
+    public void unregister() {
+        try {
+            mBinder.unregister();
+        } catch (RemoteException e) {
+            // Ignore error.
+        }
+    }
+}
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java b/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java
new file mode 100644
index 0000000..4bf7c9b
--- /dev/null
+++ b/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java
@@ -0,0 +1,48 @@
+/*
+ * 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.net.netstats.provider;
+
+import android.annotation.NonNull;
+
+/**
+ * A wrapper class of {@link INetworkStatsProvider} that hides the binder interface from exposing
+ * to outer world.
+ *
+ * @hide
+ */
+public class NetworkStatsProviderWrapper extends INetworkStatsProvider.Stub {
+    @NonNull final AbstractNetworkStatsProvider mProvider;
+
+    public NetworkStatsProviderWrapper(AbstractNetworkStatsProvider provider) {
+        mProvider = provider;
+    }
+
+    @Override
+    public void requestStatsUpdate(int token) {
+        mProvider.requestStatsUpdate(token);
+    }
+
+    @Override
+    public void setLimit(@NonNull String iface, long quotaBytes) {
+        mProvider.setLimit(iface, quotaBytes);
+    }
+
+    @Override
+    public void setAlert(long quotaBytes) {
+        mProvider.setAlert(quotaBytes);
+    }
+}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index c9ebed3..1a4dac7 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -1886,6 +1886,43 @@
     public final void writeException(@NonNull Exception e) {
         AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
 
+        int code = getExceptionCode(e);
+        writeInt(code);
+        StrictMode.clearGatheredViolations();
+        if (code == 0) {
+            if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            }
+            throw new RuntimeException(e);
+        }
+        writeString(e.getMessage());
+        final long timeNow = sParcelExceptionStackTrace ? SystemClock.elapsedRealtime() : 0;
+        if (sParcelExceptionStackTrace && (timeNow - sLastWriteExceptionStackTrace
+                > WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS)) {
+            sLastWriteExceptionStackTrace = timeNow;
+            writeStackTrace(e);
+        } else {
+            writeInt(0);
+        }
+        switch (code) {
+            case EX_SERVICE_SPECIFIC:
+                writeInt(((ServiceSpecificException) e).errorCode);
+                break;
+            case EX_PARCELABLE:
+                // Write parceled exception prefixed by length
+                final int sizePosition = dataPosition();
+                writeInt(0);
+                writeParcelable((Parcelable) e, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+                final int payloadPosition = dataPosition();
+                setDataPosition(sizePosition);
+                writeInt(payloadPosition - sizePosition);
+                setDataPosition(payloadPosition);
+                break;
+        }
+    }
+
+    /** @hide */
+    public static int getExceptionCode(@NonNull Throwable e) {
         int code = 0;
         if (e instanceof Parcelable
                 && (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) {
@@ -1909,51 +1946,25 @@
         } else if (e instanceof ServiceSpecificException) {
             code = EX_SERVICE_SPECIFIC;
         }
-        writeInt(code);
-        StrictMode.clearGatheredViolations();
-        if (code == 0) {
-            if (e instanceof RuntimeException) {
-                throw (RuntimeException) e;
-            }
-            throw new RuntimeException(e);
+        return code;
+    }
+
+    /** @hide */
+    public void writeStackTrace(@NonNull Throwable e) {
+        final int sizePosition = dataPosition();
+        writeInt(0); // Header size will be filled in later
+        StackTraceElement[] stackTrace = e.getStackTrace();
+        final int truncatedSize = Math.min(stackTrace.length, 5);
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < truncatedSize; i++) {
+            sb.append("\tat ").append(stackTrace[i]).append('\n');
         }
-        writeString(e.getMessage());
-        final long timeNow = sParcelExceptionStackTrace ? SystemClock.elapsedRealtime() : 0;
-        if (sParcelExceptionStackTrace && (timeNow - sLastWriteExceptionStackTrace
-                > WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS)) {
-            sLastWriteExceptionStackTrace = timeNow;
-            final int sizePosition = dataPosition();
-            writeInt(0); // Header size will be filled in later
-            StackTraceElement[] stackTrace = e.getStackTrace();
-            final int truncatedSize = Math.min(stackTrace.length, 5);
-            StringBuilder sb = new StringBuilder();
-            for (int i = 0; i < truncatedSize; i++) {
-                sb.append("\tat ").append(stackTrace[i]).append('\n');
-            }
-            writeString(sb.toString());
-            final int payloadPosition = dataPosition();
-            setDataPosition(sizePosition);
-            // Write stack trace header size. Used in native side to skip the header
-            writeInt(payloadPosition - sizePosition);
-            setDataPosition(payloadPosition);
-        } else {
-            writeInt(0);
-        }
-        switch (code) {
-            case EX_SERVICE_SPECIFIC:
-                writeInt(((ServiceSpecificException) e).errorCode);
-                break;
-            case EX_PARCELABLE:
-                // Write parceled exception prefixed by length
-                final int sizePosition = dataPosition();
-                writeInt(0);
-                writeParcelable((Parcelable) e, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
-                final int payloadPosition = dataPosition();
-                setDataPosition(sizePosition);
-                writeInt(payloadPosition - sizePosition);
-                setDataPosition(payloadPosition);
-                break;
-        }
+        writeString(sb.toString());
+        final int payloadPosition = dataPosition();
+        setDataPosition(sizePosition);
+        // Write stack trace header size. Used in native side to skip the header
+        writeInt(payloadPosition - sizePosition);
+        setDataPosition(payloadPosition);
     }
 
     /**
@@ -2069,14 +2080,7 @@
         if (remoteStackTrace != null) {
             RemoteException cause = new RemoteException(
                     "Remote stack trace:\n" + remoteStackTrace, null, false, false);
-            try {
-                Throwable rootCause = ExceptionUtils.getRootCause(e);
-                if (rootCause != null) {
-                    rootCause.initCause(cause);
-                }
-            } catch (RuntimeException ex) {
-                Log.e(TAG, "Cannot set cause " + cause + " for " + e, ex);
-            }
+            ExceptionUtils.appendCause(e, cause);
         }
         SneakyThrow.sneakyThrow(e);
     }
@@ -2088,6 +2092,14 @@
      * @param msg The exception message.
      */
     private Exception createException(int code, String msg) {
+        Exception exception = createExceptionOrNull(code, msg);
+        return exception != null
+                ? exception
+                : new RuntimeException("Unknown exception code: " + code + " msg " + msg);
+    }
+
+    /** @hide */
+    public Exception createExceptionOrNull(int code, String msg) {
         switch (code) {
             case EX_PARCELABLE:
                 if (readInt() > 0) {
@@ -2111,9 +2123,9 @@
                 return new UnsupportedOperationException(msg);
             case EX_SERVICE_SPECIFIC:
                 return new ServiceSpecificException(readInt(), msg);
+            default:
+                return null;
         }
-        return new RuntimeException("Unknown exception code: " + code
-                + " msg " + msg);
     }
 
     /**
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 983053b..89ddf8c 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -31,6 +31,9 @@
 import static android.system.OsConstants.S_ISREG;
 import static android.system.OsConstants.S_IWOTH;
 
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
@@ -253,6 +256,9 @@
      *             be opened with the requested mode.
      * @see #parseMode(String)
      */
+    // We can't accept a generic Executor here, since we need to use
+    // MessageQueue.addOnFileDescriptorEventListener()
+    @SuppressLint("ExecutorRegistration")
     public static ParcelFileDescriptor open(File file, int mode, Handler handler,
             final OnCloseListener listener) throws IOException {
         if (handler == null) {
@@ -268,9 +274,22 @@
         return fromFd(fd, handler, listener);
     }
 
-    /** {@hide} */
-    public static ParcelFileDescriptor fromPfd(ParcelFileDescriptor pfd, Handler handler,
-            final OnCloseListener listener) throws IOException {
+    /**
+     * Create a new ParcelFileDescriptor wrapping an already-opened file.
+     *
+     * @param pfd The already-opened file.
+     * @param handler to call listener from.
+     * @param listener to be invoked when the returned descriptor has been
+     *            closed.
+     * @return a new ParcelFileDescriptor pointing to the given file.
+     * @hide
+     */
+    @SystemApi
+    // We can't accept a generic Executor here, since we need to use
+    // MessageQueue.addOnFileDescriptorEventListener()
+    @SuppressLint("ExecutorRegistration")
+    public static @NonNull ParcelFileDescriptor wrap(@NonNull ParcelFileDescriptor pfd,
+            @NonNull Handler handler, @NonNull OnCloseListener listener) throws IOException {
         final FileDescriptor original = new FileDescriptor();
         original.setInt$(pfd.detachFd());
         return fromFd(original, handler, listener);
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index b40283f..7a837e1 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -16,17 +16,24 @@
 
 package android.os;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.util.ArrayMap;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.ArrayList;
 
 /**
@@ -339,4 +346,44 @@
 
         proto.end(token);
     }
+
+    /**
+     * Writes the content of the {@link PersistableBundle} to a {@link OutputStream}.
+     *
+     * <p>The content can be read by a {@link #readFromStream}.
+     *
+     * @see #readFromStream
+     */
+    public void writeToStream(@NonNull OutputStream outputStream) throws IOException {
+        FastXmlSerializer serializer = new FastXmlSerializer();
+        serializer.setOutput(outputStream, UTF_8.name());
+        serializer.startTag(null, "bundle");
+        try {
+            saveToXml(serializer);
+        } catch (XmlPullParserException e) {
+            throw new IOException(e);
+        }
+        serializer.endTag(null, "bundle");
+        serializer.flush();
+    }
+
+    /**
+     * Reads a {@link PersistableBundle} from an {@link InputStream}.
+     *
+     * <p>The stream must be generated by {@link #writeToStream}.
+     *
+     * @see #writeToStream
+     */
+    @NonNull
+    public static PersistableBundle readFromStream(@NonNull InputStream inputStream)
+            throws IOException {
+        try {
+            XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+            parser.setInput(inputStream, UTF_8.name());
+            parser.next();
+            return PersistableBundle.restoreFromXml(parser);
+        } catch (XmlPullParserException e) {
+            throw new IOException(e);
+        }
+    }
 }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 94623bc..3ef86ed 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -89,6 +89,12 @@
     public static final int DRM_UID = 1019;
 
     /**
+     * Defines the GID for the group that allows write access to the internal media storage.
+     * @hide
+     */
+    public static final int SDCARD_RW_GID = 1015;
+
+    /**
      * Defines the UID/GID for the group that controls VPN services.
      * @hide
      */
diff --git a/core/java/android/os/TelephonyServiceManager.java b/core/java/android/os/TelephonyServiceManager.java
index 1211dd6..064cf7d 100644
--- a/core/java/android/os/TelephonyServiceManager.java
+++ b/core/java/android/os/TelephonyServiceManager.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.content.Context;
 
 /**
  * Provides a way to register and obtain the system service binder objects managed by the telephony
@@ -51,8 +52,8 @@
         /**
          * Register a system server binding object for a service.
          */
-        public void register(@NonNull IBinder binder) {
-            ServiceManager.addService(mServiceName, binder);
+        public void register(@NonNull IBinder service) {
+            ServiceManager.addService(mServiceName, service);
         }
 
         /**
@@ -114,25 +115,123 @@
      */
     @NonNull
     public ServiceRegisterer getTelephonyServiceRegisterer() {
-        return new ServiceRegisterer("phone");
+        return new ServiceRegisterer(Context.TELEPHONY_SERVICE);
     }
 
+    /**
+     * Returns {@link ServiceRegisterer} for the telephony registry service.
+     */
+    @NonNull
+    public ServiceRegisterer getTelephonyRegistryServiceRegisterer() {
+        return new ServiceRegisterer("telephony.registry");
+    }
 
-// TODO: Add more services...
-//
-//    /**
-//     * Returns {@link ServiceRegisterer} for the "subscription" service.
-//     */
-//    @NonNull
-//    public ServiceRegisterer getSubscriptionServiceRegisterer() {
-//        return new ServiceRegisterer("isub");
-//    }
-//
-//    /**
-//     * Returns {@link ServiceRegisterer} for the "SMS" service.
-//     */
-//    @NonNull
-//    public ServiceRegisterer getSmsServiceRegisterer() {
-//        return new ServiceRegisterer("isms");
-//    }
+    /**
+     * Returns {@link ServiceRegisterer} for the telephony IMS service.
+     */
+    @NonNull
+    public ServiceRegisterer getTelephonyImsServiceRegisterer() {
+        return new ServiceRegisterer(Context.TELEPHONY_IMS_SERVICE);
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the telephony RCS message service.
+     */
+    @NonNull
+    public ServiceRegisterer getTelephonyRcsMessageServiceRegisterer() {
+        return new ServiceRegisterer(Context.TELEPHONY_RCS_MESSAGE_SERVICE);
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the subscription service.
+     */
+    @NonNull
+    public ServiceRegisterer getSubscriptionServiceRegisterer() {
+        return new ServiceRegisterer("isub");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the network policy service.
+     */
+    @NonNull
+    public ServiceRegisterer getNetworkPolicyServiceRegisterer() {
+        return new ServiceRegisterer(Context.NETWORK_POLICY_SERVICE);
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the phone sub service.
+     */
+    @NonNull
+    public ServiceRegisterer getPhoneSubServiceRegisterer() {
+        return new ServiceRegisterer("iphonesubinfo");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the opportunistic network service.
+     */
+    @NonNull
+    public ServiceRegisterer getOpportunisticNetworkServiceRegisterer() {
+        return new ServiceRegisterer("ions");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the carrier config service.
+     */
+    @NonNull
+    public ServiceRegisterer getCarrierConfigServiceRegisterer() {
+        return new ServiceRegisterer(Context.CARRIER_CONFIG_SERVICE);
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the "SMS" service.
+     */
+    @NonNull
+    public ServiceRegisterer getSmsServiceRegisterer() {
+        return new ServiceRegisterer("isms");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the eUICC controller service.
+     */
+    @NonNull
+    public ServiceRegisterer getEuiccControllerService() {
+        return new ServiceRegisterer("econtroller");
+    }
+
+    @NonNull
+    public ServiceRegisterer getEuiccCardControllerServiceRegisterer() {
+        return new ServiceRegisterer("euicc_card_controller");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the package manager service.
+     */
+    @NonNull
+    public ServiceRegisterer getPackageManagerServiceRegisterer() {
+        return new ServiceRegisterer("package");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the permission manager service.
+     */
+    @NonNull
+    public ServiceRegisterer getPermissionManagerServiceRegisterer() {
+        return new ServiceRegisterer("permissionmgr");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the ICC phone book service.
+     */
+    @NonNull
+    public ServiceRegisterer getIccPhoneBookServiceRegisterer() {
+        return new ServiceRegisterer("simphonebook");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the window service.
+     */
+    @NonNull
+    public ServiceRegisterer getWindowServiceRegisterer() {
+        return new ServiceRegisterer(Context.WINDOW_SERVICE);
+    }
 }
diff --git a/core/java/android/util/TimestampedValue.java b/core/java/android/os/TimestampedValue.java
similarity index 97%
rename from core/java/android/util/TimestampedValue.java
rename to core/java/android/os/TimestampedValue.java
index 4505673..348574e 100644
--- a/core/java/android/util/TimestampedValue.java
+++ b/core/java/android/os/TimestampedValue.java
@@ -14,13 +14,10 @@
  * limitations under the License.
  */
 
-package android.util;
+package android.os;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
 
 import java.util.Objects;
 
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index a9ddffe..16f7b39 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.IUpdateEngine;
@@ -140,8 +141,43 @@
          * {@code SWITCH_SLOT_ON_REBOOT=0}. See {@link #applyPayload}.
          */
         public static final int UPDATED_BUT_NOT_ACTIVE = 52;
+
+        /**
+         * Error code: there is not enough space on the device to apply the update. User should
+         * be prompted to free up space and re-try the update.
+         *
+         * <p>See {@link UpdateEngine#allocateSpace}.
+         */
+        public static final int NOT_ENOUGH_SPACE = 60;
+
+        /**
+         * Error code: the device is corrupted and no further updates may be applied.
+         *
+         * <p>See {@link UpdateEngine#cleanupAppliedPayload}.
+         */
+        public static final int DEVICE_CORRUPTED = 61;
     }
 
+    /** @hide */
+    @IntDef(value = {
+            ErrorCodeConstants.SUCCESS,
+            ErrorCodeConstants.ERROR,
+            ErrorCodeConstants.FILESYSTEM_COPIER_ERROR,
+            ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+            ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+            ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+            ErrorCodeConstants.KERNEL_DEVICE_OPEN_ERROR,
+            ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+            ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
+            ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+            ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+            ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR,
+            ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+            ErrorCodeConstants.NOT_ENOUGH_SPACE,
+            ErrorCodeConstants.DEVICE_CORRUPTED,
+    })
+    public @interface ErrorCode {}
+
     /**
      * Status codes for update engine. Values must agree with the ones in
      * {@code system/update_engine/client_library/include/update_engine/update_status.h}.
@@ -419,4 +455,138 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Return value of {@link #allocateSpace.}
+     */
+    public static final class AllocateSpaceResult {
+        private @ErrorCode int mErrorCode = ErrorCodeConstants.SUCCESS;
+        private long mFreeSpaceRequired = 0;
+        private AllocateSpaceResult() {}
+        /**
+         * Error code.
+         *
+         * @return The following error codes:
+         * <ul>
+         * <li>{@link ErrorCodeConstants#SUCCESS} if space has been allocated
+         *         successfully.</li>
+         * <li>{@link ErrorCodeConstants#NOT_ENOUGH_SPACE} if insufficient
+         *         space.</li>
+         * <li>Other {@link ErrorCodeConstants} for other errors.</li>
+         * </ul>
+         */
+        @ErrorCode
+        public int errorCode() {
+            return mErrorCode;
+        }
+
+        /**
+         * Estimated total space that needs to be available on the userdata partition to apply the
+         * payload (in bytes).
+         *
+         * <p>
+         * Note that in practice, more space needs to be made available before applying the payload
+         * to keep the device working.
+         *
+         * @return The following values:
+         * <ul>
+         * <li>zero if {@link #errorCode} returns {@link ErrorCodeConstants#SUCCESS}</li>
+         * <li>non-zero if {@link #errorCode} returns {@link ErrorCodeConstants#NOT_ENOUGH_SPACE}.
+         * Value is the estimated total space required on userdata partition.</li>
+         * </ul>
+         * @throws IllegalStateException if {@link #errorCode} is not one of the above.
+         *
+         */
+        public long freeSpaceRequired() {
+            if (mErrorCode == ErrorCodeConstants.SUCCESS) {
+                return 0;
+            }
+            if (mErrorCode == ErrorCodeConstants.NOT_ENOUGH_SPACE) {
+                return mFreeSpaceRequired;
+            }
+            throw new IllegalStateException(String.format(
+                    "freeSpaceRequired() is not available when error code is %d", mErrorCode));
+        }
+    }
+
+    /**
+     * Initialize partitions for a payload associated with the given payload
+     * metadata {@code payloadMetadataFilename} by preallocating required space.
+     *
+     * <p>This function should be called after payload has been verified after
+     * {@link #verifyPayloadMetadata}. This function does not verify whether
+     * the given payload is applicable or not.
+     *
+     * <p>Implementation of {@code allocateSpace} uses
+     * {@code headerKeyValuePairs} to determine whether space has been allocated
+     * for a different or same payload previously. If space has been allocated
+     * for a different payload before, space will be reallocated for the given
+     * payload. If space has been allocated for the same payload, no actions to
+     * storage devices are taken.
+     *
+     * <p>This function is synchronous and may take a non-trivial amount of
+     * time. Callers should call this function in a background thread.
+     *
+     * @param payloadMetadataFilename See {@link #verifyPayloadMetadata}.
+     * @param headerKeyValuePairs See {@link #applyPayload}.
+     * @return See {@link AllocateSpaceResult}.
+     */
+    @NonNull
+    public AllocateSpaceResult allocateSpace(
+                @NonNull String payloadMetadataFilename,
+                @NonNull String[] headerKeyValuePairs) {
+        AllocateSpaceResult result = new AllocateSpaceResult();
+        try {
+            result.mFreeSpaceRequired = mUpdateEngine.allocateSpaceForPayload(
+                    payloadMetadataFilename,
+                    headerKeyValuePairs);
+            result.mErrorCode = result.mFreeSpaceRequired == 0
+                    ? ErrorCodeConstants.SUCCESS
+                    : ErrorCodeConstants.NOT_ENOUGH_SPACE;
+            return result;
+        } catch (ServiceSpecificException e) {
+            result.mErrorCode = e.errorCode;
+            result.mFreeSpaceRequired = 0;
+            return result;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Cleanup files used by the previous update and free up space after the
+     * device has been booted successfully into the new build.
+     *
+     * <p>In particular, this function waits until delta files for snapshots for
+     * Virtual A/B update are merged to OS partitions, then delete these delta
+     * files.
+     *
+     * <p>This function is synchronous and may take a non-trivial amount of
+     * time. Callers should call this function in a background thread.
+     *
+     * <p>This function does not delete payload binaries downloaded for a
+     * non-streaming OTA update.
+     *
+     * @return One of the following:
+     * <ul>
+     * <li>{@link ErrorCodeConstants#SUCCESS} if execution is successful.</li>
+     * <li>{@link ErrorCodeConstants#ERROR} if a transient error has occurred.
+     * The device should be able to recover after a reboot. The function should
+     * be retried after the reboot.</li>
+     * <li>{@link ErrorCodeConstants#DEVICE_CORRUPTED} if a permanent error is
+     * encountered. Device is corrupted, and future updates must not be applied.
+     * The device cannot recover without flashing and factory resets.
+     * </ul>
+     *
+     * @throws ServiceSpecificException if other transient errors has occurred.
+     * A reboot may or may not help resolving the issue.
+     */
+    @ErrorCode
+    public int cleanupAppliedPayload() {
+        try {
+            return mUpdateEngine.cleanupSuccessfulUpdate();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/os/UpdateEngineCallback.java b/core/java/android/os/UpdateEngineCallback.java
index f07294e..7fe7024 100644
--- a/core/java/android/os/UpdateEngineCallback.java
+++ b/core/java/android/os/UpdateEngineCallback.java
@@ -44,5 +44,6 @@
      * unsuccessfully. The value of {@code errorCode} will be one of the
      * values from {@link UpdateEngine.ErrorCodeConstants}.
      */
-    public abstract void onPayloadApplicationComplete(int errorCode);
+    public abstract void onPayloadApplicationComplete(
+            @UpdateEngine.ErrorCode int errorCode);
 }
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 62b8953..3846f89 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -594,6 +594,8 @@
             argsForZygote.add("--mount-external-legacy");
         } else if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
             argsForZygote.add("--mount-external-pass-through");
+        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
+            argsForZygote.add("--mount-external-android-writable");
         }
 
         argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8959fcf..f0a1174 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -31,6 +31,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import android.annotation.BytesLong;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -72,6 +73,7 @@
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.sysprop.VoldProperties;
@@ -93,7 +95,6 @@
 import com.android.internal.os.FuseAppLoop;
 import com.android.internal.os.FuseUnavailableMountException;
 import com.android.internal.os.RoSystemProperties;
-import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
 
 import dalvik.system.BlockGuard;
@@ -114,6 +115,7 @@
 import java.util.Objects;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -305,109 +307,85 @@
     private final Looper mLooper;
     private final AtomicInteger mNextNonce = new AtomicInteger(0);
 
+    @GuardedBy("mDelegates")
     private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>();
 
-    private static class StorageEventListenerDelegate extends IStorageEventListener.Stub implements
-            Handler.Callback {
-        private static final int MSG_STORAGE_STATE_CHANGED = 1;
-        private static final int MSG_VOLUME_STATE_CHANGED = 2;
-        private static final int MSG_VOLUME_RECORD_CHANGED = 3;
-        private static final int MSG_VOLUME_FORGOTTEN = 4;
-        private static final int MSG_DISK_SCANNED = 5;
-        private static final int MSG_DISK_DESTROYED = 6;
+    private class StorageEventListenerDelegate extends IStorageEventListener.Stub {
+        final Executor mExecutor;
+        final StorageEventListener mListener;
+        final StorageVolumeCallback mCallback;
 
-        final StorageEventListener mCallback;
-        final Handler mHandler;
-
-        public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) {
+        public StorageEventListenerDelegate(@NonNull Executor executor,
+                @NonNull StorageEventListener listener, @NonNull StorageVolumeCallback callback) {
+            mExecutor = executor;
+            mListener = listener;
             mCallback = callback;
-            mHandler = new Handler(looper, this);
-        }
-
-        @Override
-        public boolean handleMessage(Message msg) {
-            final SomeArgs args = (SomeArgs) msg.obj;
-            switch (msg.what) {
-                case MSG_STORAGE_STATE_CHANGED:
-                    mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
-                            (String) args.arg3);
-                    args.recycle();
-                    return true;
-                case MSG_VOLUME_STATE_CHANGED:
-                    mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
-                    args.recycle();
-                    return true;
-                case MSG_VOLUME_RECORD_CHANGED:
-                    mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1);
-                    args.recycle();
-                    return true;
-                case MSG_VOLUME_FORGOTTEN:
-                    mCallback.onVolumeForgotten((String) args.arg1);
-                    args.recycle();
-                    return true;
-                case MSG_DISK_SCANNED:
-                    mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
-                    args.recycle();
-                    return true;
-                case MSG_DISK_DESTROYED:
-                    mCallback.onDiskDestroyed((DiskInfo) args.arg1);
-                    args.recycle();
-                    return true;
-            }
-            args.recycle();
-            return false;
         }
 
         @Override
         public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException {
-            // Ignored
+            mExecutor.execute(() -> {
+                mListener.onUsbMassStorageConnectionChanged(connected);
+            });
         }
 
         @Override
         public void onStorageStateChanged(String path, String oldState, String newState) {
-            final SomeArgs args = SomeArgs.obtain();
-            args.arg1 = path;
-            args.arg2 = oldState;
-            args.arg3 = newState;
-            mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
+            mExecutor.execute(() -> {
+                mListener.onStorageStateChanged(path, oldState, newState);
+
+                if (path != null) {
+                    for (StorageVolume sv : getStorageVolumes()) {
+                        if (Objects.equals(path, sv.getPath())) {
+                            mCallback.onStateChanged(sv);
+                        }
+                    }
+                }
+            });
         }
 
         @Override
         public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
-            final SomeArgs args = SomeArgs.obtain();
-            args.arg1 = vol;
-            args.argi2 = oldState;
-            args.argi3 = newState;
-            mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
+            mExecutor.execute(() -> {
+                mListener.onVolumeStateChanged(vol, oldState, newState);
+
+                final File path = vol.getPathForUser(UserHandle.myUserId());
+                if (path != null) {
+                    for (StorageVolume sv : getStorageVolumes()) {
+                        if (Objects.equals(path.getAbsolutePath(), sv.getPath())) {
+                            mCallback.onStateChanged(sv);
+                        }
+                    }
+                }
+            });
         }
 
         @Override
         public void onVolumeRecordChanged(VolumeRecord rec) {
-            final SomeArgs args = SomeArgs.obtain();
-            args.arg1 = rec;
-            mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
+            mExecutor.execute(() -> {
+                mListener.onVolumeRecordChanged(rec);
+            });
         }
 
         @Override
         public void onVolumeForgotten(String fsUuid) {
-            final SomeArgs args = SomeArgs.obtain();
-            args.arg1 = fsUuid;
-            mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
+            mExecutor.execute(() -> {
+                mListener.onVolumeForgotten(fsUuid);
+            });
         }
 
         @Override
         public void onDiskScanned(DiskInfo disk, int volumeCount) {
-            final SomeArgs args = SomeArgs.obtain();
-            args.arg1 = disk;
-            args.argi2 = volumeCount;
-            mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
+            mExecutor.execute(() -> {
+                mListener.onDiskScanned(disk, volumeCount);
+            });
         }
 
         @Override
         public void onDiskDestroyed(DiskInfo disk) throws RemoteException {
-            final SomeArgs args = SomeArgs.obtain();
-            args.arg1 = disk;
-            mHandler.obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget();
+            mExecutor.execute(() -> {
+                mListener.onDiskDestroyed(disk);
+            });
         }
     }
 
@@ -525,8 +503,8 @@
     @UnsupportedAppUsage
     public void registerListener(StorageEventListener listener) {
         synchronized (mDelegates) {
-            final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener,
-                    mLooper);
+            final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(
+                    mContext.getMainExecutor(), listener, new StorageVolumeCallback());
             try {
                 mStorageManager.registerListener(delegate);
             } catch (RemoteException e) {
@@ -548,7 +526,76 @@
         synchronized (mDelegates) {
             for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) {
                 final StorageEventListenerDelegate delegate = i.next();
-                if (delegate.mCallback == listener) {
+                if (delegate.mListener == listener) {
+                    try {
+                        mStorageManager.unregisterListener(delegate);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                    i.remove();
+                }
+            }
+        }
+    }
+
+    /**
+     * Callback that delivers {@link StorageVolume} related events.
+     * <p>
+     * For example, this can be used to detect when a volume changes to the
+     * {@link Environment#MEDIA_MOUNTED} or {@link Environment#MEDIA_UNMOUNTED}
+     * states.
+     *
+     * @see StorageManager#registerStorageVolumeCallback
+     * @see StorageManager#unregisterStorageVolumeCallback
+     */
+    public static class StorageVolumeCallback {
+        /**
+         * Called when {@link StorageVolume#getState()} changes, such as
+         * changing to the {@link Environment#MEDIA_MOUNTED} or
+         * {@link Environment#MEDIA_UNMOUNTED} states.
+         * <p>
+         * The given argument is a snapshot in time and can be used to process
+         * events in the order they occurred, or you can call
+         * {@link StorageManager#getStorageVolumes()} to observe the latest
+         * value.
+         */
+        public void onStateChanged(@NonNull StorageVolume volume) { }
+    }
+
+    /**
+     * Registers the given callback to listen for {@link StorageVolume} changes.
+     * <p>
+     * For example, this can be used to detect when a volume changes to the
+     * {@link Environment#MEDIA_MOUNTED} or {@link Environment#MEDIA_UNMOUNTED}
+     * states.
+     *
+     * @see StorageManager#unregisterStorageVolumeCallback
+     */
+    public void registerStorageVolumeCallback(@CallbackExecutor @NonNull Executor executor,
+            @NonNull StorageVolumeCallback callback) {
+        synchronized (mDelegates) {
+            final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(
+                    executor, new StorageEventListener(), callback);
+            try {
+                mStorageManager.registerListener(delegate);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mDelegates.add(delegate);
+        }
+    }
+
+    /**
+     * Unregisters the given callback from listening for {@link StorageVolume}
+     * changes.
+     *
+     * @see StorageManager#registerStorageVolumeCallback
+     */
+    public void unregisterStorageVolumeCallback(@NonNull StorageVolumeCallback callback) {
+        synchronized (mDelegates) {
+            for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) {
+                final StorageEventListenerDelegate delegate = i.next();
+                if (delegate.mCallback == callback) {
                     try {
                         mStorageManager.unregisterListener(delegate);
                     } catch (RemoteException e) {
@@ -829,7 +876,14 @@
      */
     public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException {
         Preconditions.checkNotNull(path);
-        final String pathString = path.getCanonicalPath();
+        String pathString = path.getCanonicalPath();
+        if (path.getPath().startsWith("/sdcard")) {
+            // On FUSE enabled devices, realpath(2) /sdcard is /mnt/user/<userid>/emulated/<userid>
+            // as opposed to /storage/emulated/<userid>.
+            // And vol.path below expects to match with a path starting with /storage
+            pathString = pathString.replaceFirst("^/mnt/user/[0-9]+/", "/storage/");
+        }
+
         if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) {
             return UUID_DEFAULT;
         }
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 2ab226f..e251f80 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -162,9 +163,13 @@
         mState = in.readString();
     }
 
-    /** {@hide} */
-    @UnsupportedAppUsage
-    public String getId() {
+    /**
+     * Return an opaque ID that can be used to identify this volume.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @NonNull String getId() {
         return mId;
     }
 
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 0a3c333..ef8a286 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -553,7 +553,9 @@
         /**
          * Flag indicating that a document is a directory that wants to block itself
          * from being selected when the user launches an {@link Intent#ACTION_OPEN_DOCUMENT_TREE}
-         * intent. Only valid when {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}.
+         * intent. Individual files can still be selected when launched via other intents
+         * like {@link Intent#ACTION_OPEN_DOCUMENT} and {@link Intent#ACTION_GET_CONTENT}.
+         * Only valid when {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}.
          * <p>
          * Note that this flag <em>only</em> applies to the single directory to which it is
          * applied. It does <em>not</em> block the user from selecting either a parent or
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bbaf94a..9ee4cc3 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -68,7 +68,6 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.speech.tts.TextToSpeech;
-import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 import android.util.AndroidException;
 import android.util.ArrayMap;
@@ -219,7 +218,9 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_TETHER_PROVISIONING =
+    @SystemApi
+    @TestApi
+    public static final String ACTION_TETHER_PROVISIONING_UI =
             "android.settings.TETHER_PROVISIONING_UI";
 
     /**
@@ -9698,6 +9699,8 @@
          * is interpreted as |false|.
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
 
         /**
@@ -12459,16 +12462,17 @@
 
         /**
          * Whether the Volte is enabled. If this setting is not set then we use the Carrier Config
-         * value {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
+         * value
+         * {@link android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
          * <p>
          * Type: int (0 for false, 1 for true)
          * @hide
-         * @deprecated Use {@link android.telephony.SubscriptionManager#ENHANCED_4G_MODE_ENABLED}
+         * @deprecated Use {@link android.provider.Telephony.SimInfo#ENHANCED_4G_MODE_ENABLED}
          * instead.
          */
         @Deprecated
         public static final String ENHANCED_4G_MODE_ENABLED =
-                SubscriptionManager.ENHANCED_4G_MODE_ENABLED;
+                Telephony.SimInfo.ENHANCED_4G_MODE_ENABLED;
 
         /**
          * Whether VT (Video Telephony over IMS) is enabled
@@ -12476,10 +12480,10 @@
          * Type: int (0 for false, 1 for true)
          *
          * @hide
-         * @deprecated Use {@link android.telephony.SubscriptionManager#VT_IMS_ENABLED} instead.
+         * @deprecated Use {@link android.provider.Telephony.SimInfo#VT_IMS_ENABLED} instead.
          */
         @Deprecated
-        public static final String VT_IMS_ENABLED = SubscriptionManager.VT_IMS_ENABLED;
+        public static final String VT_IMS_ENABLED = Telephony.SimInfo.VT_IMS_ENABLED;
 
         /**
          * Whether WFC is enabled
@@ -12487,10 +12491,10 @@
          * Type: int (0 for false, 1 for true)
          *
          * @hide
-         * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ENABLED} instead.
+         * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ENABLED} instead.
          */
         @Deprecated
-        public static final String WFC_IMS_ENABLED = SubscriptionManager.WFC_IMS_ENABLED;
+        public static final String WFC_IMS_ENABLED = Telephony.SimInfo.WFC_IMS_ENABLED;
 
         /**
          * WFC mode on home/non-roaming network.
@@ -12498,10 +12502,10 @@
          * Type: int - 2=Wi-Fi preferred, 1=Cellular preferred, 0=Wi-Fi only
          *
          * @hide
-         * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_MODE} instead.
+         * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_MODE} instead.
          */
         @Deprecated
-        public static final String WFC_IMS_MODE = SubscriptionManager.WFC_IMS_MODE;
+        public static final String WFC_IMS_MODE = Telephony.SimInfo.WFC_IMS_MODE;
 
         /**
          * WFC mode on roaming network.
@@ -12509,11 +12513,11 @@
          * Type: int - see {@link #WFC_IMS_MODE} for values
          *
          * @hide
-         * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_MODE}
+         * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ROAMING_MODE}
          * instead.
          */
         @Deprecated
-        public static final String WFC_IMS_ROAMING_MODE = SubscriptionManager.WFC_IMS_ROAMING_MODE;
+        public static final String WFC_IMS_ROAMING_MODE = Telephony.SimInfo.WFC_IMS_ROAMING_MODE;
 
         /**
          * Whether WFC roaming is enabled
@@ -12521,12 +12525,12 @@
          * Type: int (0 for false, 1 for true)
          *
          * @hide
-         * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_ENABLED}
+         * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ROAMING_ENABLED}
          * instead
          */
         @Deprecated
         public static final String WFC_IMS_ROAMING_ENABLED =
-                SubscriptionManager.WFC_IMS_ROAMING_ENABLED;
+                Telephony.SimInfo.WFC_IMS_ROAMING_ENABLED;
 
         /**
          * Whether user can enable/disable LTE as a preferred network. A carrier might control
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 1d9bdb8..2e7ac3f5 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -38,11 +38,13 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
+import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SmsMessage;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.UiccAccessRule;
 import android.text.TextUtils;
 import android.util.Patterns;
 
@@ -4980,5 +4982,402 @@
          */
         @NonNull
         public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo");
+
+        /**
+         * TelephonyProvider unique key column name is the subscription id.
+         * <P>Type: TEXT (String)</P>
+         */
+        public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
+
+        /**
+         * TelephonyProvider column name for a unique identifier for the subscription within the
+         * specific subscription type. For example, it contains SIM ICC Identifier subscriptions
+         * on Local SIMs. and Mac-address for Remote-SIM Subscriptions for Bluetooth devices.
+         * <P>Type: TEXT (String)</P>
+         */
+        public static final String ICC_ID = "icc_id";
+
+        /**
+         * TelephonyProvider column name for user SIM_SlOT_INDEX
+         * <P>Type: INTEGER (int)</P>
+         */
+        public static final String SIM_SLOT_INDEX = "sim_id";
+
+        /**
+         * SIM is not inserted
+         */
+        public static final int SIM_NOT_INSERTED = -1;
+
+        /**
+         * TelephonyProvider column name Subscription-type.
+         * <P>Type: INTEGER (int)</P> {@link #SUBSCRIPTION_TYPE_LOCAL_SIM} for Local-SIM
+         * Subscriptions, {@link #SUBSCRIPTION_TYPE_REMOTE_SIM} for Remote-SIM Subscriptions.
+         * Default value is 0.
+         */
+        public static final String SUBSCRIPTION_TYPE = "subscription_type";
+
+        /**
+         * This constant is to designate a subscription as a Local-SIM Subscription.
+         * <p> A Local-SIM can be a physical SIM inserted into a sim-slot in the device, or eSIM on
+         * the device.
+         * </p>
+         */
+        public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0;
+
+        /**
+         * This constant is to designate a subscription as a Remote-SIM Subscription.
+         * <p>
+         * A Remote-SIM subscription is for a SIM on a phone connected to this device via some
+         * connectivity mechanism, for example bluetooth. Similar to Local SIM, this subscription
+         * can be used for SMS, Voice and data by proxying data through the connected device.
+         * Certain data of the SIM, such as IMEI, are not accessible for Remote SIMs.
+         * </p>
+         *
+         * <p>
+         * A Remote-SIM is available only as long the phone stays connected to this device.
+         * When the phone disconnects, Remote-SIM subscription is removed from this device and is
+         * no longer known. All data associated with the subscription, such as stored SMS, call
+         * logs, contacts etc, are removed from this device.
+         * </p>
+         *
+         * <p>
+         * If the phone re-connects to this device, a new Remote-SIM subscription is created for
+         * the phone. The Subscription Id associated with the new subscription is different from
+         * the Subscription Id of the previous Remote-SIM subscription created (and removed) for the
+         * phone; i.e., new Remote-SIM subscription treats the reconnected phone as a Remote-SIM
+         * that was never seen before.
+         * </p>
+         */
+        public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1;
+
+        /**
+         * TelephonyProvider column name data_enabled_override_rules.
+         * It's a list of rules for overriding data enabled settings. The syntax is
+         * For example, "mms=nonDefault" indicates enabling data for mms in non-default
+         * subscription.
+         * "default=nonDefault&inVoiceCall" indicates enabling data for internet in non-default
+         * subscription and while is in voice call.
+         *
+         * Default value is empty string.
+         */
+        public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules";
+
+        /**
+         * TelephonyProvider column name for user displayed name.
+         * <P>Type: TEXT (String)</P>
+         */
+        public static final String DISPLAY_NAME = "display_name";
+
+        /**
+         * TelephonyProvider column name for the service provider name for the SIM.
+         * <P>Type: TEXT (String)</P>
+         */
+        public static final String CARRIER_NAME = "carrier_name";
+
+        /**
+         * TelephonyProvider column name for source of the user displayed name.
+         * <P>Type: INT (int)</P> with one of the NAME_SOURCE_XXXX values below
+         */
+        public static final String NAME_SOURCE = "name_source";
+
+        /** The name_source is the default, which is from the carrier id. */
+        public static final int NAME_SOURCE_DEFAULT = 0;
+
+        /**
+         * The name_source is from SIM EF_SPN.
+         */
+        public static final int NAME_SOURCE_SIM_SPN = 1;
+
+        /**
+         * The name_source is from user input
+         */
+        public static final int NAME_SOURCE_USER_INPUT = 2;
+
+        /**
+         * The name_source is carrier (carrier app, carrier config, etc.)
+         */
+        public static final int NAME_SOURCE_CARRIER = 3;
+
+        /**
+         * The name_source is from SIM EF_PNN.
+         */
+        public static final int NAME_SOURCE_SIM_PNN = 4;
+
+        /**
+         * TelephonyProvider column name for the color of a SIM.
+         * <P>Type: INTEGER (int)</P>
+         */
+        public static final String COLOR = "color";
+
+        /** TelephonyProvider column name for the default color of a SIM {@hide} */
+        public static final int COLOR_DEFAULT = 0;
+
+        /**
+         * TelephonyProvider column name for the phone number of a SIM.
+         * <P>Type: TEXT (String)</P>
+         */
+        public static final String NUMBER = "number";
+
+        /**
+         * TelephonyProvider column name for the number display format of a SIM.
+         * <P>Type: INTEGER (int)</P>
+         * @hide
+         */
+        public static final String DISPLAY_NUMBER_FORMAT = "display_number_format";
+
+        /**
+         * TelephonyProvider column name for the default display format of a SIM
+         * @hide
+         */
+        public static final int DISPLAY_NUMBER_DEFAULT = 1;
+
+        /**
+         * TelephonyProvider column name for whether data roaming is enabled.
+         * <P>Type: INTEGER (int)</P>
+         */
+        public static final String DATA_ROAMING = "data_roaming";
+
+        /** Indicates that data roaming is enabled for a subscription */
+        public static final int DATA_ROAMING_ENABLE = 1;
+
+        /** Indicates that data roaming is disabled for a subscription */
+        public static final int DATA_ROAMING_DISABLE = 0;
+
+        /** TelephonyProvider column name for default data roaming setting: disable */
+        public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE;
+
+        /**
+         * TelephonyProvider column name for subscription carrier id.
+         * @see TelephonyManager#getSimCarrierId()
+         * <p>Type: INTEGER (int) </p>
+         */
+        public static final String CARRIER_ID = "carrier_id";
+
+        /**
+         * A comma-separated list of EHPLMNs associated with the subscription
+         * <P>Type: TEXT (String)</P>
+         */
+        public static final String EHPLMNS = "ehplmns";
+
+        /**
+         * A comma-separated list of HPLMNs associated with the subscription
+         * <P>Type: TEXT (String)</P>
+         */
+        public static final String HPLMNS = "hplmns";
+
+        /**
+         * TelephonyProvider column name for the MCC associated with a SIM, stored as a string.
+         * <P>Type: TEXT (String)</P>
+         */
+        public static final String MCC_STRING = "mcc_string";
+
+        /**
+         * TelephonyProvider column name for the MNC associated with a SIM, stored as a string.
+         * <P>Type: TEXT (String)</P>
+         */
+        public static final String MNC_STRING = "mnc_string";
+
+        /**
+         * TelephonyProvider column name for the MCC associated with a SIM.
+         * <P>Type: INTEGER (int)</P>
+         */
+        public static final String MCC = "mcc";
+
+        /**
+         * TelephonyProvider column name for the MNC associated with a SIM.
+         * <P>Type: INTEGER (int)</P>
+         */
+        public static final String MNC = "mnc";
+
+        /**
+         * TelephonyProvider column name for the iso country code associated with a SIM.
+         * <P>Type: TEXT (String)</P>
+         */
+        public static final String ISO_COUNTRY_CODE = "iso_country_code";
+
+        /**
+         * TelephonyProvider column name for the sim provisioning status associated with a SIM.
+         * <P>Type: INTEGER (int)</P>
+         * @hide
+         */
+        public static final String SIM_PROVISIONING_STATUS = "sim_provisioning_status";
+
+        /** The sim is provisioned {@hide} */
+        public static final int SIM_PROVISIONED = 0;
+
+        /**
+         * TelephonyProvider column name for whether a subscription is embedded (that is, present on
+         * an eSIM).
+         * <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded.
+         */
+        public static final String IS_EMBEDDED = "is_embedded";
+
+        /**
+         * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of
+         * the current enabled profile on the card, while for eUICC card it is the EID of the card.
+         * <P>Type: TEXT (String)</P>
+         */
+        public static final String CARD_ID = "card_id";
+
+        /**
+         * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
+         * {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1.
+         * <p>TYPE: BLOB
+         */
+        public static final String ACCESS_RULES = "access_rules";
+
+        /**
+         * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
+         * {@link UiccAccessRule#encodeRules} but for the rules that come from CarrierConfigs.
+         * Only present if there are access rules in CarrierConfigs
+         * <p>TYPE: BLOB
+         */
+        public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS =
+                "access_rules_from_carrier_configs";
+
+        /**
+         * TelephonyProvider column name identifying whether an embedded subscription is on a
+         * removable card. Such subscriptions are marked inaccessible as soon as the current card
+         * is removed. Otherwise, they will remain accessible unless explicitly deleted. Only
+         * present if {@link #IS_EMBEDDED} is 1.
+         * <p>TYPE: INTEGER (int), 1 for removable or 0 for non-removable.
+         */
+        public static final String IS_REMOVABLE = "is_removable";
+
+        /** TelephonyProvider column name for extreme threat in CB settings */
+        public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts";
+
+        /** TelephonyProvider column name for severe threat in CB settings */
+        public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts";
+
+        /** TelephonyProvider column name for amber alert in CB settings */
+        public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts";
+
+        /** TelephonyProvider column name for emergency alert in CB settings */
+        public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts";
+
+        /** TelephonyProvider column name for alert sound duration in CB settings */
+        public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration";
+
+        /** TelephonyProvider column name for alert reminder interval in CB settings */
+        public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
+
+        /** TelephonyProvider column name for enabling vibrate in CB settings */
+        public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate";
+
+        /** TelephonyProvider column name for enabling alert speech in CB settings */
+        public static final String CB_ALERT_SPEECH = "enable_alert_speech";
+
+        /** TelephonyProvider column name for ETWS test alert in CB settings */
+        public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
+
+        /** TelephonyProvider column name for enable channel50 alert in CB settings */
+        public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
+
+        /** TelephonyProvider column name for CMAS test alert in CB settings */
+        public static final String CB_CMAS_TEST_ALERT = "enable_cmas_test_alerts";
+
+        /** TelephonyProvider column name for Opt out dialog in CB settings */
+        public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
+
+        /**
+         * TelephonyProvider column name for enable Volte.
+         *
+         * If this setting is not initialized (set to -1)  then we use the Carrier Config value
+         * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
+         */
+        public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+
+        /** TelephonyProvider column name for enable VT (Video Telephony over IMS) */
+        public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+
+        /** TelephonyProvider column name for enable Wifi calling */
+        public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+
+        /** TelephonyProvider column name for Wifi calling mode */
+        public static final String WFC_IMS_MODE = "wfc_ims_mode";
+
+        /** TelephonyProvider column name for Wifi calling mode in roaming */
+        public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
+
+        /** TelephonyProvider column name for enable Wifi calling in roaming */
+        public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+
+        /**
+         * TelephonyProvider column name for whether a subscription is opportunistic, that is,
+         * whether the network it connects to is limited in functionality or coverage.
+         * For example, CBRS.
+         * <p>Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic.
+         */
+        public static final String IS_OPPORTUNISTIC = "is_opportunistic";
+
+        /**
+         * TelephonyProvider column name for group ID. Subscriptions with same group ID
+         * are considered bundled together, and should behave as a single subscription at
+         * certain scenarios.
+         */
+        public static final String GROUP_UUID = "group_uuid";
+
+        /**
+         * TelephonyProvider column name for group owner. It's the package name who created
+         * the subscription group.
+         */
+        public static final String GROUP_OWNER = "group_owner";
+
+        /**
+         * TelephonyProvider column name for whether a subscription is metered or not, that is,
+         * whether the network it connects to charges for subscription or not. For example, paid
+         * CBRS or unpaid.
+         * @hide
+         */
+        public static final String IS_METERED = "is_metered";
+
+        /**
+         * TelephonyProvider column name for the profile class of a subscription
+         * Only present if {@link #IS_EMBEDDED} is 1.
+         * <P>Type: INTEGER (int)</P>
+         */
+        public static final String PROFILE_CLASS = "profile_class";
+
+        /**
+         * A testing profile can be pre-loaded or downloaded onto
+         * the eUICC and provides connectivity to test equipment
+         * for the purpose of testing the device and the eUICC. It
+         * is not intended to store any operator credentials.
+         */
+        public static final int PROFILE_CLASS_TESTING = 0;
+
+        /**
+         * A provisioning profile is pre-loaded onto the eUICC and
+         * provides connectivity to a mobile network solely for the
+         * purpose of provisioning profiles.
+         */
+        public static final int PROFILE_CLASS_PROVISIONING = 1;
+
+        /**
+         * An operational profile can be pre-loaded or downloaded
+         * onto the eUICC and provides services provided by the
+         * operator.
+         */
+        public static final int PROFILE_CLASS_OPERATIONAL = 2;
+
+        /**
+         * The profile class is unset. This occurs when profile class
+         * info is not available. The subscription either has no profile
+         * metadata or the profile metadata did not encode profile class.
+         */
+        public static final int PROFILE_CLASS_UNSET = -1;
+
+        /** Default profile class */
+        public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET;
+
+        /**
+         * IMSI (International Mobile Subscriber Identity).
+         * <P>Type: TEXT </P>
+         */
+        public static final String IMSI = "imsi";
+
+        /** Whether uicc applications is set to be enabled or disabled. By default it's enabled. */
+        public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled";
     }
 }
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index d51e4ca..939ae87 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -609,10 +609,12 @@
                         "must add at least 1 dataset when using header or footer");
             }
 
-            for (final Dataset dataset : mDatasets) {
-                if (dataset.getFieldInlinePresentation(0) != null) {
-                    mSupportsInlineSuggestions = true;
-                    break;
+            if (mDatasets != null) {
+                for (final Dataset dataset : mDatasets) {
+                    if (dataset.getFieldInlinePresentation(0) != null) {
+                        mSupportsInlineSuggestions = true;
+                        break;
+                    }
                 }
             }
 
diff --git a/core/java/android/telephony/WapPushManagerConnector.java b/core/java/android/telephony/WapPushManagerConnector.java
new file mode 100644
index 0000000..a9df506
--- /dev/null
+++ b/core/java/android/telephony/WapPushManagerConnector.java
@@ -0,0 +1,178 @@
+/*
+ * 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 android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * APIs for platform to connect to the WAP push manager service.
+ *
+ * <p>To start connection, {@link #bindToWapPushManagerService} should be called.
+ *
+ * <p>Upon completion {@link #unbindWapPushManagerService} should be called to unbind the service.
+ *
+ * @hide
+ */
+@SystemApi
+public final class WapPushManagerConnector {
+    private final Context mContext;
+
+    private volatile WapPushManagerConnection mConnection;
+    private volatile IWapPushManager mWapPushManager;
+    private String mWapPushManagerPackage;
+
+    /**
+     * The {@link android.content.Intent} that must be declared as handled by the
+     * WAP push manager service.
+     * @hide
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "com.android.internal.telephony.IWapPushManager";
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"RESULT_"}, value = {
+            RESULT_MESSAGE_HANDLED,
+            RESULT_APP_QUERY_FAILED,
+            RESULT_SIGNATURE_NO_MATCH,
+            RESULT_INVALID_RECEIVER_NAME,
+            RESULT_EXCEPTION_CAUGHT,
+            RESULT_FURTHER_PROCESSING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProcessMessageResult{}
+
+    /** {@link #processMessage} return value: Message is handled. */
+    public static final int RESULT_MESSAGE_HANDLED = 0x1;
+    /** {@link #processMessage} return value: Application ID or content type was not found. */
+    public static final int RESULT_APP_QUERY_FAILED = 0x2;
+    /** {@link #processMessage} return value: Receiver application signature check failed. */
+    public static final int RESULT_SIGNATURE_NO_MATCH = 0x4;
+    /** {@link #processMessage} return value: Receiver application was not found. */
+    public static final int RESULT_INVALID_RECEIVER_NAME = 0x8;
+    /** {@link #processMessage} return value: Unknown exception. */
+    public static final int RESULT_EXCEPTION_CAUGHT = 0x10;
+    /** {@link #processMessage} return value: further processing needed. */
+    public static final int RESULT_FURTHER_PROCESSING = 0x8000;
+
+    /** The application package name of the WAP push manager service. */
+    private static final String SERVICE_PACKAGE = "com.android.smspush";
+
+    public WapPushManagerConnector(@NonNull Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Binds to the WAP push manager service. This method should be called exactly once.
+     *
+     * @return {@code true} upon successfully binding to a service, {@code false} otherwise
+     */
+    public boolean bindToWapPushManagerService() {
+        Preconditions.checkState(mConnection == null);
+
+        Intent intent = new Intent(SERVICE_INTERFACE);
+        ComponentName component = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(component);
+        mConnection = new WapPushManagerConnection();
+        if (component != null
+                && mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+            mWapPushManagerPackage = component.getPackageName();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the package name of WAP push manager service application connected to,
+     * or {@code null} if not connected.
+     */
+    @Nullable
+    public String getConnectedWapPushManagerServicePackage() {
+        return mWapPushManagerPackage;
+    }
+
+    /**
+     * Processes WAP push message and triggers the {@code intent}.
+     *
+     * @see RESULT_MESSAGE_HANDLED
+     * @see RESULT_APP_QUERY_FAILED
+     * @see RESULT_SIGNATURE_NO_MATCH
+     * @see RESULT_INVALID_RECEIVER_NAME
+     * @see RESULT_EXCEPTION_CAUGHT
+     * @see RESULT_FURTHER_PROCESSING
+     */
+    @ProcessMessageResult
+    public int processMessage(
+            @NonNull String applicationId, @NonNull String contentType, @NonNull Intent intent) {
+        try {
+            return mWapPushManager.processMessage(applicationId, contentType, intent);
+        } catch (NullPointerException | RemoteException e) {
+            return RESULT_EXCEPTION_CAUGHT;
+        }
+    }
+
+    /**
+     * Unbinds the WAP push manager service. This method should be called exactly once.
+     */
+    public void unbindWapPushManagerService() {
+        Preconditions.checkNotNull(mConnection);
+
+        mContext.unbindService(mConnection);
+        mConnection = null;
+    }
+
+    private class WapPushManagerConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            // Because we have bound to an explicit
+            // service that is running in our own process, we can
+            // cast its IBinder to a concrete class and directly access it.
+            mWapPushManager = IWapPushManager.Stub.asInterface(service);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            mWapPushManager = null;
+        }
+
+        @Override
+        public void onNullBinding(ComponentName name) {
+            onServiceDisconnected(name);
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            onServiceDisconnected(name);
+        }
+    }
+}
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 3f679bba..f324113 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -387,6 +387,26 @@
     public static native int println_native(int bufID, int priority, String tag, String msg);
 
     /**
+     * Send a log message to the "radio" log buffer, which can be dumped with
+     * {@code adb logcat -b radio}.
+     *
+     * <p>Only the telephony mainline module should use it.
+     *
+     * <p>Note ART will protect {@code MODULE_LIBRARIES} system APIs from regular app code.
+     *
+     * @param priority Log priority.
+     * @param tag Used to identify the source of a log message.  It usually identifies
+     *        the class or activity where the log call occurs.
+     * @param message The message you would like logged.
+     * @hide
+     */
+    // @SystemApi(client= SystemApi.Client.MODULE_LIBRARIES) // TODO Uncomment once http://ag/9956147 is in.
+    public static int logToRadioBuffer(@Level int priority, @Nullable String tag,
+            @Nullable String message) {
+        return println_native(LOG_ID_RADIO, priority, tag, message);
+    }
+
+    /**
      * Return the maximum payload the log daemon accepts without truncation.
      * @return LOGGER_ENTRY_MAX_PAYLOAD.
      */
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 2223d14..fa994ba 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -25,6 +25,7 @@
 import android.net.NetworkInfo;
 import android.net.SntpClient;
 import android.os.SystemClock;
+import android.os.TimestampedValue;
 import android.provider.Settings;
 import android.text.TextUtils;
 
diff --git a/core/java/android/view/IDisplayWindowInsetsController.aidl b/core/java/android/view/IDisplayWindowInsetsController.aidl
new file mode 100644
index 0000000..429c3ae
--- /dev/null
+++ b/core/java/android/view/IDisplayWindowInsetsController.aidl
@@ -0,0 +1,48 @@
+/**
+ * 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 android.view;
+
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+
+/**
+ * Singular controller of insets to use when there isn't another obvious controller available.
+ * Specifically, this will take over insets control in multi-window.
+ * @hide
+ */
+oneway interface IDisplayWindowInsetsController {
+
+    /**
+     * @see IWindow#insetsChanged
+     */
+    void insetsChanged(in InsetsState insetsState);
+
+    /**
+     * @see IWindow#insetsControlChanged
+     */
+    void insetsControlChanged(in InsetsState insetsState, in InsetsSourceControl[] activeControls);
+
+    /**
+     * @see IWindow#showInsets
+     */
+    void showInsets(int types, boolean fromIme);
+
+    /**
+     * @see IWindow#hideInsets
+     */
+    void hideInsets(int types, boolean fromIme);
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9496827..993bdc4 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -35,6 +35,7 @@
 import android.view.IApplicationToken;
 import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.IDockedStackListener;
+import android.view.IDisplayWindowInsetsController;
 import android.view.IDisplayWindowListener;
 import android.view.IDisplayFoldListener;
 import android.view.IDisplayWindowRotationController;
@@ -49,6 +50,7 @@
 import android.view.IWindowSessionCallback;
 import android.view.KeyEvent;
 import android.view.InputEvent;
+import android.view.InsetsState;
 import android.view.MagnificationSpec;
 import android.view.MotionEvent;
 import android.view.InputChannel;
@@ -711,4 +713,16 @@
      * @return true if the display was successfully mirrored.
      */
     boolean mirrorDisplay(int displayId, out SurfaceControl outSurfaceControl);
+
+    /**
+     * When in multi-window mode, the provided displayWindowInsetsController will control insets
+     * animations.
+     */
+    void setDisplayWindowInsetsController(
+            int displayId, in IDisplayWindowInsetsController displayWindowInsetsController);
+
+    /**
+     * Called when a remote process modifies insets on a display window container.
+     */
+    void modifyDisplayWindowInsets(int displayId, in InsetsState state);
 }
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 771695c..a3245b9 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -257,10 +257,6 @@
         for (int i = items.size() - 1; i >= 0; i--) {
             final InsetsSourceControl control = items.valueAt(i);
             final InsetsSource source = mInitialInsetsState.getSource(control.getType());
-            if (control == null) {
-                // TODO: remove this check when we ensure the elements will not be null.
-                continue;
-            }
             final SurfaceControl leash = control.getLeash();
 
             mTmpMatrix.setTranslate(control.getSurfacePosition().x, control.getSurfacePosition().y);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 4d4ace27c..2a7a4e3 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -375,8 +375,7 @@
         }
         cancelExistingControllers(types);
 
-        final ArraySet<Integer> internalTypes = mState.toInternalType(types);
-        final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
+        final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
         final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
 
         Pair<Integer, Boolean> typesReadyPair = collectSourceControls(
@@ -441,7 +440,10 @@
                 }
                 typesReady |= InsetsState.toPublicType(consumer.getType());
             }
-            controls.put(consumer.getType(), consumer.getControl());
+            final InsetsSourceControl control = consumer.getControl();
+            if (control != null) {
+                controls.put(consumer.getType(), control);
+            }
         }
         return new Pair<>(typesReady, isReady);
     }
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index e3fed3a..ae1e579 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -19,10 +19,15 @@
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.WindowInsets.Type.IME;
 import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
 import static android.view.WindowInsets.Type.SIZE;
 import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
+import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsets.Type.indexOf;
+import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
@@ -156,11 +161,10 @@
                     && source.getType() != ITYPE_IME;
             boolean skipSystemBars = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
                     && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR);
-            boolean skipIme = source.getType() == ITYPE_IME
-                    && (legacySoftInputMode & LayoutParams.SOFT_INPUT_ADJUST_RESIZE) == 0;
             boolean skipLegacyTypes = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
-                    && (toPublicType(type) & Type.compatSystemInsets()) != 0;
-            if (skipSystemBars || skipIme || skipLegacyTypes || skipNonImeInImeMode) {
+                    && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR
+                            || type == ITYPE_IME);
+            if (skipSystemBars || skipLegacyTypes || skipNonImeInImeMode) {
                 typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible();
                 continue;
             }
@@ -175,8 +179,11 @@
                         typeMaxInsetsMap, null /* typeSideMap */, null /* typeVisibilityMap */);
             }
         }
+        final int softInputAdjustMode = legacySoftInputMode & SOFT_INPUT_MASK_ADJUST;
         return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
-                alwaysConsumeSystemBars, cutout);
+                alwaysConsumeSystemBars, cutout, softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE
+                        ? systemBars() | ime()
+                        : systemBars());
     }
 
     private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index f6d6522..ff8455a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -677,6 +677,22 @@
         }
 
         /**
+         * Set the initial visibility for the SurfaceControl.
+         *
+         * @param hidden Whether the Surface is initially HIDDEN.
+         * @hide
+         */
+        @NonNull
+        public Builder setHidden(boolean hidden) {
+            if (hidden) {
+                mFlags |= HIDDEN;
+            } else {
+                mFlags &= ~HIDDEN;
+            }
+            return this;
+        }
+
+        /**
          * Set a parent surface for our new SurfaceControl.
          *
          * Child surfaces are constrained to the onscreen region of their parent.
@@ -789,8 +805,7 @@
      * @param name     The surface name, must not be null.
      * @param w        The surface initial width.
      * @param h        The surface initial height.
-     * @param flags    The surface creation flags.  Should always include {@link #HIDDEN}
-     *                 in the creation flags.
+     * @param flags    The surface creation flags.
      * @param metadata Initial metadata.
      * @throws throws OutOfResourcesException If the SurfaceControl cannot be created.
      */
@@ -801,15 +816,6 @@
             throw new IllegalArgumentException("name must not be null");
         }
 
-        if ((flags & SurfaceControl.HIDDEN) == 0) {
-            Log.w(TAG, "Surfaces should always be created with the HIDDEN flag set "
-                    + "to ensure that they are not made visible prematurely before "
-                    + "all of the surface's properties have been configured.  "
-                    + "Set the other properties and make the surface visible within "
-                    + "a transaction.  New surface name: " + name,
-                    new Throwable());
-        }
-
         mName = name;
         mWidth = w;
         mHeight = h;
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 52ea2b2..1782544 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -379,7 +379,7 @@
                  * This gets called on a RenderThread worker thread, so members accessed here must
                  * be protected by a lock.
                  */
-                final boolean useBLAST = ViewRootImpl.USE_BLAST_BUFFERQUEUE;
+                final boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER;
                 viewRoot.registerRtFrameCallback(frame -> {
                     try {
                         final SurfaceControl.Transaction t = useBLAST ?
@@ -1107,7 +1107,7 @@
 
     private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t,
             Rect position, long frameNumber) {
-        if (frameNumber > 0 && ViewRootImpl.USE_BLAST_BUFFERQUEUE == false) {
+        if (frameNumber > 0 && !WindowManagerGlobal.USE_BLAST_ADAPTER) {
             final ViewRootImpl viewRoot = getViewRootImpl();
 
             t.deferTransactionUntilSurface(surface, viewRoot.mSurface,
@@ -1125,7 +1125,7 @@
     }
 
     private void setParentSpaceRectangle(Rect position, long frameNumber) {
-        final boolean useBLAST = ViewRootImpl.USE_BLAST_BUFFERQUEUE;
+        final boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER;
         final ViewRootImpl viewRoot = getViewRootImpl();
         final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() :
             mRtTransaction;
@@ -1186,7 +1186,7 @@
 
         @Override
         public void positionLost(long frameNumber) {
-            boolean useBLAST = ViewRootImpl.USE_BLAST_BUFFERQUEUE;
+            boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER;
             if (DEBUG) {
                 Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
                         System.identityHashCode(this), frameNumber));
@@ -1524,7 +1524,7 @@
     @Override
     public void invalidate(boolean invalidateCache) {
         super.invalidate(invalidateCache);
-        if (ViewRootImpl.USE_BLAST_BUFFERQUEUE == false) {
+        if (!WindowManagerGlobal.USE_BLAST_ADAPTER) {
             return;
         }
         final ViewRootImpl viewRoot = getViewRootImpl();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 522ff9a..bf8dc65 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -192,11 +192,6 @@
     private static final boolean MT_RENDERER_AVAILABLE = true;
 
     /**
-     * @hide
-     */
-    public static final boolean USE_BLAST_BUFFERQUEUE = false;
-
-    /**
      * If set to 2, the view system will switch from using rectangles retrieved from window to
      * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets
      * directly from the full configuration, enabling richer information about the insets state, as
@@ -1312,7 +1307,7 @@
             }
             mWindowAttributes.privateFlags |= compatibleWindowFlag;
 
-            if (USE_BLAST_BUFFERQUEUE) {
+            if (WindowManagerGlobal.USE_BLAST_ADAPTER) {
                 mWindowAttributes.privateFlags =
                     WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
             }
@@ -7273,7 +7268,7 @@
                 mPendingStableInsets, mPendingBackDropFrame, mPendingDisplayCutout,
                 mPendingMergedConfiguration, mSurfaceControl, mTempInsets);
         if (mSurfaceControl.isValid()) {
-            if (USE_BLAST_BUFFERQUEUE == false) {
+            if (!WindowManagerGlobal.USE_BLAST_ADAPTER) {
                 mSurface.copyFrom(mSurfaceControl);
             } else {
                 mSurface.transferFrom(getOrCreateBLASTSurface(
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index a9cc50f..9df131d 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -27,8 +27,8 @@
 import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
 import static android.view.WindowInsets.Type.TAPPABLE_ELEMENT;
 import static android.view.WindowInsets.Type.all;
-import static android.view.WindowInsets.Type.compatSystemInsets;
 import static android.view.WindowInsets.Type.indexOf;
+import static android.view.WindowInsets.Type.systemBars;
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
@@ -87,6 +87,8 @@
     private final boolean mStableInsetsConsumed;
     private final boolean mDisplayCutoutConsumed;
 
+    private final int mCompatInsetTypes;
+
     /**
      * Since new insets may be added in the future that existing apps couldn't
      * know about, this fully empty constant shouldn't be made available to apps
@@ -112,7 +114,7 @@
             boolean isRound, boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) {
         this(createCompatTypeMap(systemWindowInsetsRect), createCompatTypeMap(stableInsetsRect),
                 createCompatVisibilityMap(createCompatTypeMap(systemWindowInsetsRect)),
-                isRound, alwaysConsumeSystemBars, displayCutout);
+                isRound, alwaysConsumeSystemBars, displayCutout, systemBars());
     }
 
     /**
@@ -131,7 +133,7 @@
             @Nullable Insets[] typeMaxInsetsMap,
             boolean[] typeVisibilityMap,
             boolean isRound,
-            boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) {
+            boolean alwaysConsumeSystemBars, DisplayCutout displayCutout, int compatInsetTypes) {
         mSystemWindowInsetsConsumed = typeInsetsMap == null;
         mTypeInsetsMap = mSystemWindowInsetsConsumed
                 ? new Insets[SIZE]
@@ -145,6 +147,7 @@
         mTypeVisibilityMap = typeVisibilityMap;
         mIsRound = isRound;
         mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
+        mCompatInsetTypes = compatInsetTypes;
 
         mDisplayCutoutConsumed = displayCutout == null;
         mDisplayCutout = (mDisplayCutoutConsumed || displayCutout.isEmpty())
@@ -160,7 +163,8 @@
         this(src.mSystemWindowInsetsConsumed ? null : src.mTypeInsetsMap,
                 src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
                 src.mTypeVisibilityMap, src.mIsRound,
-                src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src));
+                src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src),
+                src.mCompatInsetTypes);
     }
 
     private static DisplayCutout displayCutoutCopyConstructorArgument(WindowInsets w) {
@@ -211,7 +215,8 @@
     /** @hide */
     @UnsupportedAppUsage
     public WindowInsets(Rect systemWindowInsets) {
-        this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null);
+        this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null,
+                systemBars());
     }
 
     /**
@@ -280,7 +285,7 @@
      */
     @NonNull
     public Insets getSystemWindowInsets() {
-        return getInsets(mTypeInsetsMap, compatSystemInsets());
+        return getInsets(mTypeInsetsMap, mCompatInsetTypes);
     }
 
     /**
@@ -439,7 +444,8 @@
                 mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
                 mTypeVisibilityMap,
                 mIsRound, mAlwaysConsumeSystemBars,
-                null /* displayCutout */);
+                null /* displayCutout */,
+                mCompatInsetTypes);
     }
 
 
@@ -485,7 +491,8 @@
         return new WindowInsets(null, mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
                 mTypeVisibilityMap,
                 mIsRound, mAlwaysConsumeSystemBars,
-                displayCutoutCopyConstructorArgument(this));
+                displayCutoutCopyConstructorArgument(this),
+                mCompatInsetTypes);
     }
 
     // TODO(b/119190588): replace @code with @link below
@@ -555,7 +562,7 @@
      */
     @NonNull
     public Insets getStableInsets() {
-        return getInsets(mTypeMaxInsetsMap, compatSystemInsets());
+        return getInsets(mTypeMaxInsetsMap, mCompatInsetTypes);
     }
 
     /**
@@ -733,7 +740,8 @@
     public WindowInsets consumeStableInsets() {
         return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap, null,
                 mTypeVisibilityMap, mIsRound, mAlwaysConsumeSystemBars,
-                displayCutoutCopyConstructorArgument(this));
+                displayCutoutCopyConstructorArgument(this),
+                mCompatInsetTypes);
     }
 
     /**
@@ -817,7 +825,8 @@
                         ? null
                         : mDisplayCutout == null
                                 ? DisplayCutout.NO_CUTOUT
-                                : mDisplayCutout.inset(left, top, right, bottom));
+                                : mDisplayCutout.inset(left, top, right, bottom),
+                mCompatInsetTypes);
     }
 
     @Override
@@ -1134,7 +1143,8 @@
         public WindowInsets build() {
             return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
                     mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap,
-                    mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout);
+                    mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout,
+                    systemBars());
         }
     }
 
@@ -1271,15 +1281,6 @@
         }
 
         /**
-         * @return Inset types representing the list of bars that traditionally were denoted as
-         *         system insets.
-         * @hide
-         */
-        static @InsetsType int compatSystemInsets() {
-            return STATUS_BARS | NAVIGATION_BARS | IME;
-        }
-
-        /**
          * @return All inset types combined.
          *
          * TODO: Figure out if this makes sense at all, mixing e.g {@link #systemGestures()} and
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 9578002..7d5564e 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -57,6 +57,12 @@
     private static final String TAG = "WindowManager";
 
     /**
+     * This flag controls whether ViewRootImpl will utilize the Blast Adapter
+     * to send buffer updates to SurfaceFlinger
+     */
+    public static final boolean USE_BLAST_ADAPTER = false;
+
+    /**
      * The user is navigating with keys (not the touch screen), so
      * navigational focus should be shown.
      */
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index ff31bcc..2e5a4b5 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -31,6 +31,7 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
+import android.app.RemoteAction;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -1209,6 +1210,61 @@
     }
 
     /**
+     * Register the provided {@link RemoteAction} with the given actionId
+     *
+     * @param action The remote action to be registered with the given actionId as system action.
+     * @param actionId The id uniquely identify the system action.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+    public void registerSystemAction(@NonNull RemoteAction action, int actionId) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.registerSystemAction(action, actionId);
+
+            if (DEBUG) {
+                Log.i(LOG_TAG, "System action " + action.getTitle() + " is registered.");
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error registering system action " + action.getTitle() + " ", re);
+        }
+    }
+
+   /**
+     * Unregister a system action with the given actionId
+     *
+     * @param actionId The id uniquely identify the system action to be unregistered.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+    public void unregisterSystemAction(int actionId) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.unregisterSystemAction(actionId);
+
+            if (DEBUG) {
+                Log.i(LOG_TAG, "System action with actionId " + actionId + " is unregistered.");
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error unregistering system action with actionId " + actionId + " ", re);
+        }
+    }
+
+    /**
      * Notifies that the accessibility button in the system's navigation area has been clicked
      *
      * @param displayId The logical display id.
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 36515b3..392db57 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -16,6 +16,7 @@
 
 package android.view.accessibility;
 
+import android.app.RemoteAction;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.accessibilityservice.IAccessibilityServiceClient;
@@ -82,4 +83,7 @@
     int getAccessibilityWindowId(IBinder windowToken);
 
     long getRecommendedTimeoutMillis();
+
+    oneway void registerSystemAction(in RemoteAction action, int actionId);
+    oneway void unregisterSystemAction(int actionId);
 }
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index f9ba34e..0a0d05c 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -61,7 +61,6 @@
 
     private int mShortcutType;
     private List<AccessibilityButtonTarget> mTargets = new ArrayList<>();
-    private List<AccessibilityButtonTarget> mReadyToBeDisabledTargets = new ArrayList<>();
     private AlertDialog mAlertDialog;
     private TargetAdapter mTargetAdapter;
 
@@ -117,7 +116,6 @@
                 ACCESSIBILITY_BUTTON);
         mTargets.addAll(getServiceTargets(this, mShortcutType));
 
-        // TODO(b/146815548): Will add title to separate which one type
         mTargetAdapter = new TargetAdapter(mTargets);
         mAlertDialog = new AlertDialog.Builder(this)
                 .setAdapter(mTargetAdapter, /* listener= */ null)
@@ -269,8 +267,10 @@
 
             switch (target.getFragmentType()) {
                 case AccessibilityServiceFragmentType.LEGACY:
+                    updateLegacyActionItemVisibility(context, holder);
+                    break;
                 case AccessibilityServiceFragmentType.INVISIBLE:
-                    updateLegacyOrInvisibleActionItemVisibility(context, holder);
+                    updateInvisibleActionItemVisibility(context, holder);
                     break;
                 case AccessibilityServiceFragmentType.INTUITIVE:
                     updateIntuitiveActionItemVisibility(context, holder, target);
@@ -283,9 +283,21 @@
             }
         }
 
-        private void updateLegacyOrInvisibleActionItemVisibility(@NonNull Context context,
+        private void updateLegacyActionItemVisibility(@NonNull Context context,
                 @NonNull ViewHolder holder) {
-            final boolean isEditMenuMode = mShortcutMenuMode == ShortcutMenuMode.EDIT;
+            final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
+
+            holder.mLabelView.setEnabled(!isEditMenuMode);
+            holder.mViewItem.setEnabled(!isEditMenuMode);
+            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);
+        }
+
+        private void updateInvisibleActionItemVisibility(@NonNull Context context,
+                @NonNull ViewHolder holder) {
+            final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
 
             holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
             holder.mViewItem.setVisibility(View.VISIBLE);
@@ -295,7 +307,7 @@
 
         private void updateIntuitiveActionItemVisibility(@NonNull Context context,
                 @NonNull ViewHolder holder, AccessibilityButtonTarget target) {
-            final boolean isEditMenuMode = mShortcutMenuMode == ShortcutMenuMode.EDIT;
+            final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
 
             holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
             holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
@@ -306,7 +318,7 @@
 
         private void updateBounceActionItemVisibility(@NonNull Context context,
                 @NonNull ViewHolder holder) {
-            final boolean isEditMenuMode = mShortcutMenuMode == ShortcutMenuMode.EDIT;
+            final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
 
             holder.mViewItem.setImageDrawable(
                     isEditMenuMode ? context.getDrawable(R.drawable.ic_delete_item)
@@ -383,20 +395,23 @@
     }
 
     private void onTargetDeleted(AdapterView<?> parent, View view, int position, long id) {
-        // TODO(b/147027236): Will discuss with UX designer what UX behavior about deleting item
-        //  is good for user.
-        mReadyToBeDisabledTargets.add(mTargets.get(position));
-        mTargets.remove(position);
-        mTargetAdapter.notifyDataSetChanged();
+        // TODO(b/146967898): disable service when deleting the target and the target only have
+        //  last one shortcut item, only remove it from shortcut list otherwise.
+        if ((mShortcutType == ACCESSIBILITY_BUTTON) && (mTargets.get(position).mFragmentType
+                != AccessibilityServiceFragmentType.LEGACY)) {
+            mTargets.remove(position);
+            mTargetAdapter.notifyDataSetChanged();
+        }
+
+        if (mTargets.isEmpty()) {
+            mAlertDialog.dismiss();
+        }
     }
 
     private void onCancelButtonClicked() {
-        resetAndUpdateTargets();
-
         mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH);
         mTargetAdapter.notifyDataSetChanged();
 
-        mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
         mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
                 getString(R.string.edit_accessibility_shortcut_menu_button));
 
@@ -407,49 +422,19 @@
         mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.EDIT);
         mTargetAdapter.notifyDataSetChanged();
 
-        mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setText(
+        mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
                 getString(R.string.cancel_accessibility_shortcut_menu_button));
-        mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.VISIBLE);
-        mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
-                getString(R.string.save_accessibility_shortcut_menu_button));
-
-        updateDialogListeners();
-    }
-
-    private void onSaveButtonClicked() {
-        disableTargets();
-        resetAndUpdateTargets();
-
-        mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH);
-        mTargetAdapter.notifyDataSetChanged();
-
-        mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
-        mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
-                getString(R.string.edit_accessibility_shortcut_menu_button));
 
         updateDialogListeners();
     }
 
     private void updateDialogListeners() {
         final boolean isEditMenuMode =
-                mTargetAdapter.getShortcutMenuMode() == ShortcutMenuMode.EDIT;
+                (mTargetAdapter.getShortcutMenuMode() == ShortcutMenuMode.EDIT);
 
-        mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener(
-                view -> onCancelButtonClicked());
         mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(
-                isEditMenuMode ? view -> onSaveButtonClicked() : view -> onEditButtonClicked());
+                isEditMenuMode ? view -> onCancelButtonClicked() : view -> onEditButtonClicked());
         mAlertDialog.getListView().setOnItemClickListener(
                 isEditMenuMode ? this::onTargetDeleted : this::onTargetSelected);
     }
-
-    private void disableTargets() {
-        for (AccessibilityButtonTarget service : mReadyToBeDisabledTargets) {
-            // TODO(b/146967898): disable services.
-        }
-    }
-
-    private void resetAndUpdateTargets() {
-        mTargets.clear();
-        mTargets.addAll(getServiceTargets(this, mShortcutType));
-    }
 }
diff --git a/core/java/com/android/internal/compat/AndroidBuildClassifier.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
new file mode 100644
index 0000000..0b937fa
--- /dev/null
+++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
@@ -0,0 +1,34 @@
+/*
+ * 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.internal.compat;
+
+import android.os.Build;
+
+/**
+ * Platform private class for determining the type of Android build installed.
+ *
+ */
+public class AndroidBuildClassifier {
+
+    public boolean isDebuggableBuild() {
+        return Build.IS_DEBUGGABLE;
+    }
+
+    public boolean isFinalBuild() {
+        return "REL".equals(Build.VERSION.CODENAME);
+    }
+}
diff --git a/core/java/com/android/internal/compat/IOverrideValidator.aidl b/core/java/com/android/internal/compat/IOverrideValidator.aidl
new file mode 100644
index 0000000..add4708
--- /dev/null
+++ b/core/java/com/android/internal/compat/IOverrideValidator.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.internal.compat;
+
+import android.content.pm.ApplicationInfo;
+
+import com.android.internal.compat.OverrideAllowedState;
+
+/**
+ * Platform private API for determining whether a changeId can be overridden.
+ *
+ * {@hide}
+ */
+interface IOverrideValidator
+{
+    /**
+     * Validation function.
+     * @param changeId    id of the change to be toggled on or off.
+     * @param packageName package of the app for which the change should be overridden.
+     * @return {@link OverrideAllowedState} specifying whether the change can be overridden for
+     * the given package or a reason why not.
+     */
+    OverrideAllowedState getOverrideAllowedState(long changeId, String packageName);
+}
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 7dcb12c..4c203d3 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.compat;
 
 import android.content.pm.ApplicationInfo;
+import com.android.internal.compat.IOverrideValidator;
 import java.util.Map;
 
 parcelable CompatibilityChangeConfig;
@@ -195,4 +196,9 @@
      * @return An array of {@link CompatChangeInfo} known to the service.
      */
     CompatibilityChangeInfo[] listAllChanges();
+
+    /**
+     * Get an instance that can determine whether a changeid can be overridden for a package name.
+     */
+    IOverrideValidator getOverrideValidator();
 }
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.aidl b/core/java/com/android/internal/compat/OverrideAllowedState.aidl
new file mode 100644
index 0000000..10ceac7
--- /dev/null
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.internal.compat;
+
+parcelable OverrideAllowedState;
\ No newline at end of file
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
new file mode 100644
index 0000000..56216c2
--- /dev/null
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -0,0 +1,153 @@
+/*
+ * 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.internal.compat;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class contains all the possible override allowed states.
+ */
+public final class OverrideAllowedState implements Parcelable {
+    @IntDef({
+            ALLOWED,
+            DISABLED_NOT_DEBUGGABLE,
+            DISABLED_NON_TARGET_SDK,
+            DISABLED_TARGET_SDK_TOO_HIGH,
+            PACKAGE_DOES_NOT_EXIST
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {
+    }
+
+    /**
+     * Change can be overridden.
+     */
+    public static final int ALLOWED = 0;
+    /**
+     * Change cannot be overridden, due to the app not being debuggable.
+     */
+    public static final int DISABLED_NOT_DEBUGGABLE = 1;
+    /**
+     * Change cannot be overridden, due to the build being non-debuggable and the change being
+     * non-targetSdk.
+     */
+    public static final int DISABLED_NON_TARGET_SDK = 2;
+    /**
+     * Change cannot be overridden, due to the app's targetSdk being above the change's targetSdk.
+     */
+    public static final int DISABLED_TARGET_SDK_TOO_HIGH = 3;
+    /**
+     * Package does not exist.
+     */
+    public static final int PACKAGE_DOES_NOT_EXIST = 4;
+
+    @State
+    public final int state;
+    public final int appTargetSdk;
+    public final int changeIdTargetSdk;
+
+    private OverrideAllowedState(Parcel parcel) {
+        state = parcel.readInt();
+        appTargetSdk = parcel.readInt();
+        changeIdTargetSdk = parcel.readInt();
+    }
+
+    public OverrideAllowedState(@State int state, int appTargetSdk, int changeIdTargetSdk) {
+        this.state = state;
+        this.appTargetSdk = appTargetSdk;
+        this.changeIdTargetSdk = changeIdTargetSdk;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(state);
+        out.writeInt(appTargetSdk);
+        out.writeInt(changeIdTargetSdk);
+    }
+
+    /**
+     * Enforces the policy for overriding compat changes.
+     *
+     * @param changeId    the change id that was attempted to be overridden.
+     * @param packageName the package for which the attempt was made.
+     * @throws SecurityException if the policy forbids this operation.
+     */
+    public void enforce(long changeId, String packageName)
+            throws SecurityException {
+        switch (state) {
+            case ALLOWED:
+                return;
+            case DISABLED_NOT_DEBUGGABLE:
+                throw new SecurityException(
+                        "Cannot override a change on a non-debuggable app and user build.");
+            case DISABLED_NON_TARGET_SDK:
+                throw new SecurityException(
+                        "Cannot override a default enabled/disabled change on a user build.");
+            case DISABLED_TARGET_SDK_TOO_HIGH:
+                throw new SecurityException(String.format(
+                        "Cannot override %1$d for %2$s because the app's targetSdk (%3$d) is "
+                                + "above the change's targetSdk threshold (%4$d)",
+                        changeId, packageName, appTargetSdk, changeIdTargetSdk));
+            case PACKAGE_DOES_NOT_EXIST:
+                throw new SecurityException(String.format(
+                        "Cannot override %1$d for %2$s because the package does not exist, and "
+                                + "the change is targetSdk gated.",
+                        changeId, packageName));
+        }
+    }
+
+    public static final @NonNull
+            Parcelable.Creator<OverrideAllowedState> CREATOR =
+                new Parcelable.Creator<OverrideAllowedState>() {
+                public OverrideAllowedState createFromParcel(Parcel parcel) {
+                    OverrideAllowedState info = new OverrideAllowedState(parcel);
+                    return info;
+                }
+
+                public OverrideAllowedState[] newArray(int size) {
+                    return new OverrideAllowedState[size];
+                }
+            };
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof OverrideAllowedState)) {
+            return false;
+        }
+        OverrideAllowedState otherState = (OverrideAllowedState) obj;
+        return state == otherState.state
+                && appTargetSdk == otherState.appTargetSdk
+                && changeIdTargetSdk == otherState.changeIdTargetSdk;
+    }
+}
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java
index b250578..9f15d89 100644
--- a/core/java/com/android/internal/infra/AndroidFuture.java
+++ b/core/java/com/android/internal/infra/AndroidFuture.java
@@ -75,6 +75,7 @@
 
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = AndroidFuture.class.getSimpleName();
+    private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
 
     private final @NonNull Object mLock = new Object();
     @GuardedBy("mLock")
@@ -95,15 +96,7 @@
             // Done
             if (in.readBoolean()) {
                 // Failed
-                try {
-                    in.readException();
-                } catch (Throwable e) {
-                    completeExceptionally(e);
-                }
-                if (!isCompletedExceptionally()) {
-                    throw new IllegalStateException(
-                            "Error unparceling AndroidFuture: exception expected");
-                }
+                completeExceptionally(unparcelException(in));
             } else {
                 // Success
                 complete((T) in.readValue(null));
@@ -512,14 +505,9 @@
             T result;
             try {
                 result = get();
-            } catch (Exception t) {
-                // Exceptions coming out of get() are wrapped in ExecutionException, which is not
-                // handled by Parcel.
-                if (t instanceof ExecutionException && t.getCause() instanceof Exception) {
-                    t = (Exception) t.getCause();
-                }
+            } catch (Throwable t) {
                 dest.writeBoolean(true);
-                dest.writeException(t);
+                parcelException(dest, unwrapExecutionException(t));
                 return;
             }
             dest.writeBoolean(false);
@@ -528,22 +516,76 @@
             dest.writeStrongBinder(new IAndroidFuture.Stub() {
                 @Override
                 public void complete(AndroidFuture resultContainer) {
+                    boolean changed;
                     try {
-                        AndroidFuture.this.complete((T) resultContainer.get());
+                        changed = AndroidFuture.this.complete((T) resultContainer.get());
                     } catch (Throwable t) {
-                        // If resultContainer was completed exceptionally, get() wraps the
-                        // underlying exception in an ExecutionException. Unwrap it now to avoid
-                        // double-layering ExecutionExceptions.
-                        if (t instanceof ExecutionException && t.getCause() != null) {
-                            t = t.getCause();
-                        }
-                        completeExceptionally(t);
+                        changed = completeExceptionally(unwrapExecutionException(t));
+                    }
+                    if (!changed) {
+                        Log.w(LOG_TAG, "Remote result " + resultContainer
+                                + " ignored, as local future is already completed: "
+                                + AndroidFuture.this);
                     }
                 }
             }.asBinder());
         }
     }
 
+    /**
+     * Exceptions coming out of {@link #get} are wrapped in {@link ExecutionException}
+     */
+    Throwable unwrapExecutionException(Throwable t) {
+        return t instanceof ExecutionException
+                ? t.getCause()
+                : t;
+    }
+
+    /**
+     * Alternative to {@link Parcel#writeException} that stores the stack trace, in a
+     * way consistent with the binder IPC exception propagation behavior.
+     */
+    private static void parcelException(Parcel p, @Nullable Throwable t) {
+        p.writeBoolean(t == null);
+        if (t == null) {
+            return;
+        }
+
+        p.writeInt(Parcel.getExceptionCode(t));
+        p.writeString(t.getClass().getName());
+        p.writeString(t.getMessage());
+        p.writeStackTrace(t);
+        parcelException(p, t.getCause());
+    }
+
+    /**
+     * @see #parcelException
+     */
+    private static @Nullable Throwable unparcelException(Parcel p) {
+        if (p.readBoolean()) {
+            return null;
+        }
+
+        int exCode = p.readInt();
+        String cls = p.readString();
+        String msg = p.readString();
+        String stackTrace = p.readInt() > 0 ? p.readString() : "\t<stack trace unavailable>";
+        msg += "\n" + stackTrace;
+
+        Exception ex = p.createExceptionOrNull(exCode, msg);
+        if (ex == null) {
+            ex = new RuntimeException(cls + ": " + msg);
+        }
+        ex.setStackTrace(EMPTY_STACK_TRACE);
+
+        Throwable cause = unparcelException(p);
+        if (cause != null) {
+            ex.initCause(ex);
+        }
+
+        return ex;
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 2248b88..f0a346a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -145,6 +145,11 @@
     /** The lower file system should be bind mounted directly on external storage */
     public static final int MOUNT_EXTERNAL_PASS_THROUGH = IVold.REMOUNT_MODE_PASS_THROUGH;
 
+    /** Use the regular scoped storage filesystem, but Android/ should be writable.
+     * Used to support the applications hosting DownloadManager and the MTP server.
+     */
+    public static final int MOUNT_EXTERNAL_ANDROID_WRITABLE = IVold.REMOUNT_MODE_ANDROID_WRITABLE;
+
     /** Number of bytes sent to the Zygote over USAP pipes or the pool event FD */
     static final int USAP_MANAGEMENT_MESSAGE_BYTES = 8;
 
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index d349954..37f570b 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -376,6 +376,8 @@
                 mMountExternal = Zygote.MOUNT_EXTERNAL_LEGACY;
             } else if (arg.equals("--mount-external-pass-through")) {
                 mMountExternal = Zygote.MOUNT_EXTERNAL_PASS_THROUGH;
+            } else if (arg.equals("--mount-external-android-writable")) {
+                mMountExternal = Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE;
             } else if (arg.equals("--query-abi-list")) {
                 mAbiListQuery = true;
             } else if (arg.equals("--get-pid")) {
diff --git a/telephony/java/com/android/internal/telephony/IWapPushManager.aidl b/core/java/com/android/internal/telephony/IWapPushManager.aidl
similarity index 82%
rename from telephony/java/com/android/internal/telephony/IWapPushManager.aidl
rename to core/java/com/android/internal/telephony/IWapPushManager.aidl
index 1c3df65..9f6851b 100644
--- a/telephony/java/com/android/internal/telephony/IWapPushManager.aidl
+++ b/core/java/com/android/internal/telephony/IWapPushManager.aidl
@@ -18,6 +18,7 @@
 
 import android.content.Intent;
 
+/** @hide */
 interface IWapPushManager {
     /**
      * Processes WAP push message and triggers the receiver application registered
@@ -26,11 +27,10 @@
     int processMessage(String app_id, String content_type, in Intent intent);
 
     /**
-     * Add receiver application into the application ID table.
-     * Returns true if inserting the information is successfull. Inserting the duplicated
+     * Adds receiver application into the application ID table.
+     * Returns true if inserting the information is successful. Inserting duplicated
      * record in the application ID table is not allowed. Use update/delete method.
      */
-    @UnsupportedAppUsage
     boolean addPackage(String x_app_id, String content_type,
             String package_name, String class_name,
             int app_type, boolean need_signature, boolean further_processing);
@@ -39,17 +39,14 @@
      * Updates receiver application that is last added.
      * Returns true if updating the information is successfull.
      */
-    @UnsupportedAppUsage
     boolean updatePackage(String x_app_id, String content_type,
             String package_name, String class_name,
             int app_type, boolean need_signature, boolean further_processing);
 
     /**
-     * Delites receiver application information.
+     * Deletes receiver application information.
      * Returns true if deleting is successfull.
      */
-    @UnsupportedAppUsage
     boolean deletePackage(String x_app_id, String content_type,
-                            String package_name, String class_name);
+            String package_name, String class_name);
 }
-
diff --git a/core/java/com/android/internal/telephony/WapPushManagerParams.java b/core/java/com/android/internal/telephony/WapPushManagerParams.java
new file mode 100644
index 0000000..eafb8f1
--- /dev/null
+++ b/core/java/com/android/internal/telephony/WapPushManagerParams.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.telephony.WapPushManagerConnector;
+
+/**
+ * WapPushManager constant value definitions.
+ * @hide
+ */
+public class WapPushManagerParams {
+    /**
+     * Application type activity
+     */
+    public static final int APP_TYPE_ACTIVITY = 0;
+
+    /**
+     * Application type service
+     */
+    public static final int APP_TYPE_SERVICE = 1;
+
+    /**
+     * Process Message return value
+     * Message is handled
+     */
+    public static final int MESSAGE_HANDLED = WapPushManagerConnector.RESULT_MESSAGE_HANDLED;
+
+    /**
+     * Process Message return value
+     * Application ID or content type was not found in the application ID table
+     */
+    public static final int APP_QUERY_FAILED = WapPushManagerConnector.RESULT_APP_QUERY_FAILED;
+
+    /**
+     * Process Message return value
+     * Receiver application signature check failed
+     */
+    public static final int SIGNATURE_NO_MATCH = WapPushManagerConnector.RESULT_SIGNATURE_NO_MATCH;
+
+    /**
+     * Process Message return value
+     * Receiver application was not found
+     */
+    public static final int INVALID_RECEIVER_NAME =
+            WapPushManagerConnector.RESULT_INVALID_RECEIVER_NAME;
+
+    /**
+     * Process Message return value
+     * Unknown exception
+     */
+    public static final int EXCEPTION_CAUGHT = WapPushManagerConnector.RESULT_EXCEPTION_CAUGHT;
+
+    /**
+     * Process Message return value
+     * Need further processing after WapPushManager message processing
+     */
+    public static final int FURTHER_PROCESSING = WapPushManagerConnector.RESULT_FURTHER_PROCESSING;
+}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 8a59c99..74b481c 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -218,6 +218,7 @@
     final ArrayMap<String, ArraySet<String>> mAllowedAssociations = new ArrayMap<>();
 
     private final ArraySet<String> mBugreportWhitelistedPackages = new ArraySet<>();
+    private final ArraySet<String> mAppDataIsolationWhitelistedApps = new ArraySet<>();
 
     // Map of packagesNames to userTypes. Stored temporarily until cleared by UserManagerService().
     private ArrayMap<String, Set<String>> mPackageToUserTypeWhitelist = new ArrayMap<>();
@@ -389,6 +390,10 @@
         return mRollbackWhitelistedPackages;
     }
 
+    public ArraySet<String> getAppDataIsolationWhitelistedApps() {
+        return mAppDataIsolationWhitelistedApps;
+    }
+
     /**
      * Gets map of packagesNames to userTypes, dictating on which user types each package should be
      * initially installed, and then removes this map from SystemConfig.
@@ -1045,6 +1050,16 @@
                         }
                         XmlUtils.skipCurrentTag(parser);
                     } break;
+                    case "app-data-isolation-whitelisted-app": {
+                        String pkgname = parser.getAttributeValue(null, "package");
+                        if (pkgname == null) {
+                            Slog.w(TAG, "<" + name + "> without package in " + permFile
+                                    + " at " + parser.getPositionDescription());
+                        } else {
+                            mAppDataIsolationWhitelistedApps.add(pkgname);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
                     case "bugreport-whitelisted": {
                         String pkgname = parser.getAttributeValue(null, "package");
                         if (pkgname == null) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index d17d0a4..5039213 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -319,7 +319,8 @@
   MOUNT_EXTERNAL_INSTALLER = 5,
   MOUNT_EXTERNAL_FULL = 6,
   MOUNT_EXTERNAL_PASS_THROUGH = 7,
-  MOUNT_EXTERNAL_COUNT = 8
+  MOUNT_EXTERNAL_ANDROID_WRITABLE = 8,
+  MOUNT_EXTERNAL_COUNT = 9
 };
 
 // The order of entries here must be kept in sync with MountExternalKind enum values.
@@ -331,6 +332,8 @@
   "/mnt/runtime/write",   // MOUNT_EXTERNAL_LEGACY
   "/mnt/runtime/write",   // MOUNT_EXTERNAL_INSTALLER
   "/mnt/runtime/full",    // MOUNT_EXTERNAL_FULL
+  "/mnt/runtime/full",    // MOUNT_EXTERNAL_PASS_THROUGH (only used w/ FUSE)
+  "/mnt/runtime/full",    // MOUNT_EXTERNAL_ANDROID_WRITABLE (only used w/ FUSE)
 };
 
 // Must match values in com.android.internal.os.Zygote.
@@ -755,12 +758,7 @@
              multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn);
 
   if (isFuse) {
-    if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH || mount_mode ==
-        MOUNT_EXTERNAL_INSTALLER || mount_mode == MOUNT_EXTERNAL_FULL) {
-      // For now, MediaProvider, installers and "full" get the pass_through mount
-      // view, which is currently identical to the sdcardfs write view.
-      //
-      // TODO(b/146189163): scope down MOUNT_EXTERNAL_INSTALLER
+    if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
       BindMount(pass_through_source, "/storage", fail_fn);
     } else {
       BindMount(user_source, "/storage", fail_fn);
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 5624f45..082a289 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -33,27 +33,27 @@
 
 // Static whitelist of open paths that the zygote is allowed to keep open.
 static const char* kPathWhitelist[] = {
-  "/apex/com.android.appsearch/javalib/framework-appsearch.jar",
-  "/apex/com.android.conscrypt/javalib/conscrypt.jar",
-  "/apex/com.android.ipsec/javalib/ike.jar",
-  "/apex/com.android.media/javalib/updatable-media.jar",
-  "/apex/com.android.mediaprovider/javalib/framework-mediaprovider.jar",
-  "/apex/com.android.os.statsd/javalib/framework-statsd.jar",
-  "/apex/com.android.sdkext/javalib/framework-sdkext.jar",
-  "/apex/com.android.wifi/javalib/framework-wifi.jar",
-  "/apex/com.android.tethering/javalib/framework-tethering.jar",
-  "/dev/null",
-  "/dev/socket/zygote",
-  "/dev/socket/zygote_secondary",
-  "/dev/socket/usap_pool_primary",
-  "/dev/socket/usap_pool_secondary",
-  "/dev/socket/webview_zygote",
-  "/dev/socket/heapprofd",
-  "/sys/kernel/debug/tracing/trace_marker",
-  "/system/framework/framework-res.apk",
-  "/dev/urandom",
-  "/dev/ion",
-  "/dev/dri/renderD129", // Fixes b/31172436
+        "/apex/com.android.appsearch/javalib/framework-appsearch.jar",
+        "/apex/com.android.conscrypt/javalib/conscrypt.jar",
+        "/apex/com.android.ipsec/javalib/ike.jar",
+        "/apex/com.android.media/javalib/updatable-media.jar",
+        "/apex/com.android.mediaprovider/javalib/framework-mediaprovider.jar",
+        "/apex/com.android.os.statsd/javalib/framework-statsd.jar",
+        "/apex/com.android.sdkext/javalib/framework-sdkextensions.jar",
+        "/apex/com.android.wifi/javalib/framework-wifi.jar",
+        "/apex/com.android.tethering/javalib/framework-tethering.jar",
+        "/dev/null",
+        "/dev/socket/zygote",
+        "/dev/socket/zygote_secondary",
+        "/dev/socket/usap_pool_primary",
+        "/dev/socket/usap_pool_secondary",
+        "/dev/socket/webview_zygote",
+        "/dev/socket/heapprofd",
+        "/sys/kernel/debug/tracing/trace_marker",
+        "/system/framework/framework-res.apk",
+        "/dev/urandom",
+        "/dev/ion",
+        "/dev/dri/renderD129", // Fixes b/31172436
 };
 
 static const char kFdPath[] = "/proc/self/fd";
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index d5a3b5e..b83b31c 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -702,6 +702,12 @@
     // CATEGORY: SETTINGS
     // OS: R
     ACTION_DASHBOARD_VISIBLE_TIME = 1729;
+
+    // ACTION: Allow "Access all files" for an app
+    APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_ALLOW = 1730;
+
+    // ACTION: Deny "Access all files" for an app
+    APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_DENY = 1731;
 }
 
 /**
@@ -2542,4 +2548,9 @@
     // OS: R
     FUELGAUGE_BATTERY_SHARE = 1821;
 
+    // OPEN: Settings -> Apps & Notifications -> Special App Access
+    // CATEGORY: SETTINGS
+    // OS: R
+    MANAGE_EXTERNAL_STORAGE = 1822;
+
 }
diff --git a/core/proto/android/server/animationadapter.proto b/core/proto/android/server/animationadapter.proto
index 70627ed..c6925f4 100644
--- a/core/proto/android/server/animationadapter.proto
+++ b/core/proto/android/server/animationadapter.proto
@@ -50,6 +50,7 @@
     optional WindowAnimationSpecProto window = 1;
     optional MoveAnimationSpecProto move = 2;
     optional AlphaAnimationSpecProto alpha = 3;
+    optional RotationAnimationSpecProto rotate = 4;
 }
 
 /* represents WindowAnimationSpec */
@@ -76,3 +77,12 @@
     optional float to = 2;
     optional int64 duration_ms = 3;
 }
+
+/* represents RotationAnimationSpec */
+message RotationAnimationSpecProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional float start_luma = 1;
+    optional float end_luma = 2;
+    optional int64 duration_ms = 3;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c38c8a7..5aa0a2d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -456,7 +456,6 @@
     <protected-broadcast android:name="android.intent.action.internal_sim_state_changed" />
     <protected-broadcast android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
     <protected-broadcast android:name="android.intent.action.PRECISE_CALL_STATE" />
-    <protected-broadcast android:name="android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.intent.action.SUBSCRIPTION_PHONE_STATE" />
     <protected-broadcast android:name="android.intent.action.USER_INFO_CHANGED" />
     <protected-broadcast android:name="android.intent.action.USER_UNLOCKED" />
@@ -4756,6 +4755,10 @@
     <!-- Allows input events to be monitored. Very dangerous!  @hide -->
     <permission android:name="android.permission.MONITOR_INPUT"
                 android:protectionLevel="signature" />
+    <!--  Allows the caller to change the associations between input devices and displays.
+        Very dangerous! @hide -->
+    <permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT"
+                android:protectionLevel="signature" />
 
     <!-- Allows query of any normal app on the device, regardless of manifest declarations. -->
     <permission android:name="android.permission.QUERY_ALL_PACKAGES"
diff --git a/core/res/res/anim/screen_rotate_0_enter.xml b/core/res/res/anim/screen_rotate_0_enter.xml
index 93cf365..629be7e 100644
--- a/core/res/res/anim/screen_rotate_0_enter.xml
+++ b/core/res/res/anim/screen_rotate_0_enter.xml
@@ -1,25 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
-** Copyright 2010, 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.
-*/
--->
+  ~ 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.
+  -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false">
-    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:duration="@android:integer/config_shortAnimTime" />
+    android:shareInterpolator="false">
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:interpolator="@interpolator/screen_rotation_alpha_in"
+        android:fillEnabled="true"
+        android:fillBefore="true" android:fillAfter="true"
+        android:duration="@android:integer/config_screen_rotation_fade_in" />
 </set>
diff --git a/core/res/res/anim/screen_rotate_0_exit.xml b/core/res/res/anim/screen_rotate_0_exit.xml
index 37d5a411..fa046a0 100644
--- a/core/res/res/anim/screen_rotate_0_exit.xml
+++ b/core/res/res/anim/screen_rotate_0_exit.xml
@@ -1,22 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
-** Copyright 2010, 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.
-*/
--->
+  ~ 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.
+  -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false">
+    android:shareInterpolator="false">
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+        android:interpolator="@interpolator/screen_rotation_alpha_out"
+        android:fillEnabled="true"
+        android:fillBefore="true" android:fillAfter="true"
+        android:duration="@android:integer/config_screen_rotation_fade_out" />
 </set>
diff --git a/core/res/res/anim/screen_rotate_0_frame.xml b/core/res/res/anim/screen_rotate_0_frame.xml
deleted file mode 100644
index 5ea9bf8..0000000
--- a/core/res/res/anim/screen_rotate_0_frame.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false">
-    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:duration="@android:integer/config_shortAnimTime" />
-</set>
diff --git a/core/res/res/anim/screen_rotate_180_enter.xml b/core/res/res/anim/screen_rotate_180_enter.xml
index 688a8d5..889a615 100644
--- a/core/res/res/anim/screen_rotate_180_enter.xml
+++ b/core/res/res/anim/screen_rotate_180_enter.xml
@@ -18,11 +18,11 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false">
+    android:shareInterpolator="false">
     <rotate android:fromDegrees="180" android:toDegrees="0"
-            android:pivotX="50%" android:pivotY="50%"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_mediumAnimTime" />
+        android:pivotX="50%" android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="@android:integer/config_screen_rotation_total_180" />
 </set>
diff --git a/core/res/res/anim/screen_rotate_180_exit.xml b/core/res/res/anim/screen_rotate_180_exit.xml
index 58a1868..766fcfa 100644
--- a/core/res/res/anim/screen_rotate_180_exit.xml
+++ b/core/res/res/anim/screen_rotate_180_exit.xml
@@ -18,11 +18,11 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false">
+    android:shareInterpolator="false">
     <rotate android:fromDegrees="0" android:toDegrees="-180"
-            android:pivotX="50%" android:pivotY="50%"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_mediumAnimTime" />
+        android:pivotX="50%" android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="@android:integer/config_screen_rotation_total_180" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/screen_rotate_alpha.xml b/core/res/res/anim/screen_rotate_alpha.xml
index c49ef9c..2cac982 100644
--- a/core/res/res/anim/screen_rotate_alpha.xml
+++ b/core/res/res/anim/screen_rotate_alpha.xml
@@ -20,8 +20,8 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:shareInterpolator="false">
     <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-           android:interpolator="@interpolator/decelerate_quint"
+           android:interpolator="@interpolator/screen_rotation_alpha_out"
            android:fillEnabled="true"
            android:fillBefore="true" android:fillAfter="true"
-           android:duration="@android:integer/config_mediumAnimTime" />
+           android:duration="@android:integer/config_screen_rotation_fade_out" />
 </set>
diff --git a/core/res/res/anim/screen_rotate_minus_90_enter.xml b/core/res/res/anim/screen_rotate_minus_90_enter.xml
index b16d5fc..87fd25e 100644
--- a/core/res/res/anim/screen_rotate_minus_90_enter.xml
+++ b/core/res/res/anim/screen_rotate_minus_90_enter.xml
@@ -18,19 +18,17 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false">
-    <!-- Version for two-phase anim
+    android:shareInterpolator="false">
     <rotate android:fromDegrees="-90" android:toDegrees="0"
-            android:pivotX="50%" android:pivotY="50%"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_longAnimTime" />
-    -->
-    <rotate android:fromDegrees="-90" android:toDegrees="0"
-            android:pivotX="50%" android:pivotY="50%"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:duration="@android:integer/config_mediumAnimTime" />
+        android:pivotX="50%" android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="@android:integer/config_screen_rotation_total_90" />
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:fillEnabled="true"
+        android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/screen_rotation_alpha_in"
+        android:startOffset="@android:integer/config_screen_rotation_fade_in_delay"
+        android:duration="@android:integer/config_screen_rotation_fade_in" />
 </set>
diff --git a/core/res/res/anim/screen_rotate_minus_90_exit.xml b/core/res/res/anim/screen_rotate_minus_90_exit.xml
index 0927dd3..c3aee14 100644
--- a/core/res/res/anim/screen_rotate_minus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_minus_90_exit.xml
@@ -18,26 +18,16 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false">
-    <!-- Version for two-phase animation
+    android:shareInterpolator="false">
     <rotate android:fromDegrees="0" android:toDegrees="90"
-            android:pivotX="50%" android:pivotY="50%"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_longAnimTime" />
-    -->
-    <scale android:fromXScale="100%" android:toXScale="100%p"
-            android:fromYScale="100%" android:toYScale="100%p"
-            android:pivotX="50%" android:pivotY="50%"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_mediumAnimTime" />
-    <rotate android:fromDegrees="0" android:toDegrees="90"
-            android:pivotX="50%" android:pivotY="50%"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_mediumAnimTime" />
+        android:pivotX="50%" android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="@android:integer/config_screen_rotation_total_90" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+        android:fillEnabled="true"
+        android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/screen_rotation_alpha_out"
+        android:duration="@android:integer/config_screen_rotation_fade_out" />
 </set>
diff --git a/core/res/res/anim/screen_rotate_plus_90_enter.xml b/core/res/res/anim/screen_rotate_plus_90_enter.xml
index 86a8d24..8849db4 100644
--- a/core/res/res/anim/screen_rotate_plus_90_enter.xml
+++ b/core/res/res/anim/screen_rotate_plus_90_enter.xml
@@ -18,19 +18,16 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false">
-    <!-- Version for two-phase animation
+    android:shareInterpolator="false">
     <rotate android:fromDegrees="90" android:toDegrees="0"
-            android:pivotX="50%" android:pivotY="50%"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_longAnimTime" />
-    -->
-    <rotate android:fromDegrees="90" android:toDegrees="0"
-            android:pivotX="50%" android:pivotY="50%"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_mediumAnimTime" />
+        android:pivotX="50%" android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="@android:integer/config_screen_rotation_total_90" />
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:fillEnabled="true"
+        android:interpolator="@interpolator/screen_rotation_alpha_in"
+        android:startOffset="@android:integer/config_screen_rotation_fade_in_delay"
+        android:duration="@android:integer/config_screen_rotation_fade_in" />
 </set>
diff --git a/core/res/res/anim/screen_rotate_plus_90_exit.xml b/core/res/res/anim/screen_rotate_plus_90_exit.xml
index fd786f9..de84c3b 100644
--- a/core/res/res/anim/screen_rotate_plus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_plus_90_exit.xml
@@ -18,26 +18,16 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false">
-    <!-- Version for two-phase animation
+    android:shareInterpolator="false">
     <rotate android:fromDegrees="0" android:toDegrees="-90"
-            android:pivotX="50%" android:pivotY="50%"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_longAnimTime" />
-    -->
-    <scale android:fromXScale="100%" android:toXScale="100%p"
-            android:fromYScale="100%" android:toYScale="100%p"
-            android:pivotX="50%" android:pivotY="50%"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_mediumAnimTime" />
-    <rotate android:fromDegrees="0" android:toDegrees="-90"
-            android:pivotX="50%" android:pivotY="50%"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_mediumAnimTime" />
+        android:pivotX="50%" android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="@android:integer/config_screen_rotation_total_90" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+        android:interpolator="@interpolator/screen_rotation_alpha_out"
+        android:fillEnabled="true"
+        android:fillBefore="true" android:fillAfter="true"
+        android:duration="@android:integer/config_screen_rotation_fade_out" />
 </set>
diff --git a/core/res/res/interpolator/screen_rotation_alpha_in.xml b/core/res/res/interpolator/screen_rotation_alpha_in.xml
new file mode 100644
index 0000000..9c566a7
--- /dev/null
+++ b/core/res/res/interpolator/screen_rotation_alpha_in.xml
@@ -0,0 +1,22 @@
+<?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.
+  -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0.15"
+    android:controlY1="0.45"
+    android:controlX2="0.33"
+    android:controlY2="1"/>
diff --git a/core/res/res/interpolator/screen_rotation_alpha_out.xml b/core/res/res/interpolator/screen_rotation_alpha_out.xml
new file mode 100644
index 0000000..73a37d4
--- /dev/null
+++ b/core/res/res/interpolator/screen_rotation_alpha_out.xml
@@ -0,0 +1,22 @@
+<?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.
+  -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0.57"
+    android:controlY1="0"
+    android:controlX2="0.71"
+    android:controlY2=".43"/>
diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml
index 1edd2cd..d19e313 100644
--- a/core/res/res/layout/accessibility_button_chooser_item.xml
+++ b/core/res/res/layout/accessibility_button_chooser_item.xml
@@ -36,7 +36,7 @@
         android:id="@+id/accessibility_button_target_label"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginStart="8dp"
+        android:layout_marginStart="14dp"
         android:layout_weight="1"
         android:textColor="?attr/textColorPrimary"/>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6691d4c..245aed1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -147,6 +147,24 @@
     <integer name="config_activityShortDur">150</integer>
     <integer name="config_activityDefaultDur">220</integer>
 
+    <!-- Fade out time for screen rotation -->
+    <integer name="config_screen_rotation_fade_out">116</integer>
+
+    <!-- Fade in time for screen rotation -->
+    <integer name="config_screen_rotation_fade_in">233</integer>
+
+    <!-- Fade in delay time for screen rotation -->
+    <integer name="config_screen_rotation_fade_in_delay">100</integer>
+
+    <!-- Total time for 90 degree screen rotation animations -->
+    <integer name="config_screen_rotation_total_90">333</integer>
+
+    <!-- Total time for 180 degree screen rotation animation -->
+    <integer name="config_screen_rotation_total_180">433</integer>
+
+    <!-- Total time for the rotation background color transition -->
+    <integer name="config_screen_rotation_color_transition">200</integer>
+
     <!-- The duration (in milliseconds) of the tooltip show/hide animations. -->
     <integer name="config_tooltipAnimTime">150</integer>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 66267d1..a0e4064 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4358,10 +4358,7 @@
     <string name="accessibility_shortcut_menu_button">Empty</string>
 
     <!-- Text in button that edit the accessibility shortcut menu. [CHAR LIMIT=100] -->
-    <string name="edit_accessibility_shortcut_menu_button">Edit</string>
-
-    <!-- Text in button that save the accessibility shortcut menu changed status. [CHAR LIMIT=100] -->
-    <string name="save_accessibility_shortcut_menu_button">Save</string>
+    <string name="edit_accessibility_shortcut_menu_button">Edit shortcuts</string>
 
     <!-- Text in button that cancel the accessibility shortcut menu changed status. [CHAR LIMIT=100] -->
     <string name="cancel_accessibility_shortcut_menu_button">Cancel</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 01bd510..973d5f6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1805,7 +1805,6 @@
   <!-- From services -->
   <java-symbol type="anim" name="screen_rotate_0_enter" />
   <java-symbol type="anim" name="screen_rotate_0_exit" />
-  <java-symbol type="anim" name="screen_rotate_0_frame" />
   <java-symbol type="anim" name="screen_rotate_180_enter" />
   <java-symbol type="anim" name="screen_rotate_180_exit" />
   <java-symbol type="anim" name="screen_rotate_180_frame" />
@@ -1981,6 +1980,7 @@
   <java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
   <java-symbol type="integer" name="config_brightness_ramp_rate_fast" />
   <java-symbol type="integer" name="config_brightness_ramp_rate_slow" />
+  <java-symbol type="integer" name="config_screen_rotation_color_transition" />
   <java-symbol type="layout" name="am_compat_mode_dialog" />
   <java-symbol type="layout" name="launch_warning" />
   <java-symbol type="layout" name="safe_mode" />
@@ -3212,7 +3212,6 @@
   <java-symbol type="id" name="accessibility_button_target_switch_item" />
   <java-symbol type="string" name="accessibility_magnification_chooser_text" />
   <java-symbol type="string" name="edit_accessibility_shortcut_menu_button" />
-  <java-symbol type="string" name="save_accessibility_shortcut_menu_button" />
   <java-symbol type="string" name="cancel_accessibility_shortcut_menu_button" />
 
   <java-symbol type="drawable" name="ic_accessibility_magnification" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java
index f0a8367..a296ca2 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.connectivitymanagertest;
 
+import android.net.IpConfiguration;
 import android.net.IpConfiguration.IpAssignment;
 import android.net.IpConfiguration.ProxySettings;
 import android.net.LinkAddress;
@@ -136,7 +137,7 @@
         config.enterpriseConfig.setPhase2Method(phase2);
         config.enterpriseConfig.setIdentity(identity);
         config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);
-        config.enterpriseConfig.setCaCertificateAlias(caCert);
+        config.enterpriseConfig.setCaCertificateAliases(new String[] {caCert});
         config.enterpriseConfig.setClientCertificateAlias(clientCert);
         return config;
     }
@@ -147,8 +148,12 @@
     private static WifiConfiguration createGenericConfig(String ssid) {
         WifiConfiguration config = new WifiConfiguration();
         config.SSID = quotedString(ssid);
-        config.setIpAssignment(IpAssignment.DHCP);
-        config.setProxySettings(ProxySettings.NONE);
+
+        IpConfiguration ipConfiguration = config.getIpConfiguration();
+        ipConfiguration.setIpAssignment(IpAssignment.DHCP);
+        ipConfiguration.setProxySettings(ProxySettings.NONE);
+        config.setIpConfiguration(ipConfiguration);
+
         return config;
     }
 
@@ -237,6 +242,7 @@
                 throw new IllegalArgumentException();
         }
 
+        IpConfiguration ipConfiguration = config.getIpConfiguration();
         if (jsonConfig.has("ip")) {
             StaticIpConfiguration staticIpConfig = new StaticIpConfiguration();
 
@@ -247,13 +253,14 @@
             staticIpConfig.dnsServers.add(getInetAddress(jsonConfig.getString("dns1")));
             staticIpConfig.dnsServers.add(getInetAddress(jsonConfig.getString("dns2")));
 
-            config.setIpAssignment(IpAssignment.STATIC);
-            config.setStaticIpConfiguration(staticIpConfig);
+            ipConfiguration.setIpAssignment(IpAssignment.STATIC);
+            ipConfiguration.setStaticIpConfiguration(staticIpConfig);
         } else {
-            config.setIpAssignment(IpAssignment.DHCP);
+            ipConfiguration.setIpAssignment(IpAssignment.DHCP);
         }
+        ipConfiguration.setProxySettings(ProxySettings.NONE);
+        config.setIpConfiguration(ipConfiguration);
 
-        config.setProxySettings(ProxySettings.NONE);
         return config;
     }
 
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
index 2989df8..5d42915 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
@@ -268,7 +268,7 @@
         File snd_stat = new File (root_filepath + "tcp_snd");
         int tx = BandwidthTestUtil.parseIntValueFromFile(snd_stat);
         NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
-        stats.addValues(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT,
+        stats.addEntry(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT,
                 NetworkStats.TAG_NONE, rx, 0, tx, 0, 0);
         return stats;
     }
diff --git a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
index 707d7b3..239f971 100644
--- a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
+++ b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
@@ -54,7 +54,7 @@
             recycle.txBytes = 150000;
             recycle.txPackets = 1500;
             recycle.operations = 0;
-            mNetworkStats.addValues(recycle);
+            mNetworkStats.addEntry(recycle);
             if (recycle.set == 1) {
                 uid++;
             }
@@ -70,7 +70,7 @@
             recycle.txBytes = 180000 * mSize;
             recycle.txPackets = 1200 * mSize;
             recycle.operations = 0;
-            mNetworkStats.addValues(recycle);
+            mNetworkStats.addEntry(recycle);
         }
     }
 
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index beaaa37..d8b527c 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -438,8 +438,7 @@
     }
 
     private static ClientTransaction newStopTransaction(Activity activity) {
-        final StopActivityItem stopStateRequest =
-                StopActivityItem.obtain(false /* showWindow */, 0 /* configChanges */);
+        final StopActivityItem stopStateRequest = StopActivityItem.obtain(0 /* configChanges */);
 
         final ClientTransaction transaction = newTransaction(activity);
         transaction.setLifecycleStateRequest(stopStateRequest);
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 37d21f0..4b29d59 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -274,30 +274,15 @@
 
     @Test
     public void testRecycleStopItem() {
-        StopActivityItem emptyItem = StopActivityItem.obtain(false, 0);
-        StopActivityItem item = StopActivityItem.obtain(true, 4);
+        StopActivityItem emptyItem = StopActivityItem.obtain(0);
+        StopActivityItem item = StopActivityItem.obtain(4);
         assertNotSame(item, emptyItem);
         assertFalse(item.equals(emptyItem));
 
         item.recycle();
         assertEquals(item, emptyItem);
 
-        StopActivityItem item2 = StopActivityItem.obtain(true, 3);
-        assertSame(item, item2);
-        assertFalse(item2.equals(emptyItem));
-    }
-
-    @Test
-    public void testRecycleWindowVisibleItem() {
-        WindowVisibilityItem emptyItem = WindowVisibilityItem.obtain(false);
-        WindowVisibilityItem item = WindowVisibilityItem.obtain(true);
-        assertNotSame(item, emptyItem);
-        assertFalse(item.equals(emptyItem));
-
-        item.recycle();
-        assertEquals(item, emptyItem);
-
-        WindowVisibilityItem item2 = WindowVisibilityItem.obtain(true);
+        StopActivityItem item2 = StopActivityItem.obtain(3);
         assertSame(item, item2);
         assertFalse(item2.equals(emptyItem));
     }
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 39bf742..ecea901 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -61,6 +61,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -180,31 +181,6 @@
     }
 
     @Test
-    public void testWindowVisibilityChange() {
-        // Write to parcel
-        WindowVisibilityItem item = WindowVisibilityItem.obtain(true /* showWindow */);
-        writeAndPrepareForReading(item);
-
-        // Read from parcel and assert
-        WindowVisibilityItem result = WindowVisibilityItem.CREATOR.createFromParcel(mParcel);
-
-        assertEquals(item.hashCode(), result.hashCode());
-        assertTrue(item.equals(result));
-
-        // Check different value
-        item = WindowVisibilityItem.obtain(false);
-
-        mParcel = Parcel.obtain();
-        writeAndPrepareForReading(item);
-
-        // Read from parcel and assert
-        result = WindowVisibilityItem.CREATOR.createFromParcel(mParcel);
-
-        assertEquals(item.hashCode(), result.hashCode());
-        assertTrue(item.equals(result));
-    }
-
-    @Test
     public void testDestroy() {
         DestroyActivityItem item = DestroyActivityItem.obtain(true /* finished */,
                 135 /* configChanges */);
@@ -299,8 +275,7 @@
     @Test
     public void testStop() {
         // Write to parcel
-        StopActivityItem item = StopActivityItem.obtain(true /* showWindow */,
-                14 /* configChanges */);
+        StopActivityItem item = StopActivityItem.obtain(14 /* configChanges */);
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
@@ -311,14 +286,26 @@
     }
 
     @Test
+    public void testStart() {
+        // Write to parcel
+        StartActivityItem item = StartActivityItem.obtain();
+        writeAndPrepareForReading(item);
+
+        // Read from parcel and assert
+        StartActivityItem result = StartActivityItem.CREATOR.createFromParcel(mParcel);
+
+        assertEquals(item.hashCode(), result.hashCode());
+        assertEquals(item, result);
+    }
+
+    @Test
     public void testClientTransaction() {
         // Write to parcel
-        WindowVisibilityItem callback1 = WindowVisibilityItem.obtain(true);
+        NewIntentItem callback1 = NewIntentItem.obtain(new ArrayList<>(), true);
         ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
                 config());
 
-        StopActivityItem lifecycleRequest = StopActivityItem.obtain(true /* showWindow */,
-                78 /* configChanges */);
+        StopActivityItem lifecycleRequest = StopActivityItem.obtain(78 /* configChanges */);
 
         IApplicationThread appThread = new StubAppThread();
         Binder activityToken = new Binder();
@@ -340,7 +327,7 @@
     @Test
     public void testClientTransactionCallbacksOnly() {
         // Write to parcel
-        WindowVisibilityItem callback1 = WindowVisibilityItem.obtain(true);
+        NewIntentItem callback1 = NewIntentItem.obtain(new ArrayList<>(), true);
         ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
                 config());
 
@@ -363,8 +350,7 @@
     @Test
     public void testClientTransactionLifecycleOnly() {
         // Write to parcel
-        StopActivityItem lifecycleRequest = StopActivityItem.obtain(true /* showWindow */,
-                78 /* configChanges */);
+        StopActivityItem lifecycleRequest = StopActivityItem.obtain(78 /* configChanges */);
 
         IApplicationThread appThread = new StubAppThread();
         Binder activityToken = new Binder();
diff --git a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java
index de6f8f7..750ffa1 100644
--- a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java
@@ -22,7 +22,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
 
 import org.junit.Test;
 
diff --git a/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java
index 9b3d0c9..b88c36f 100644
--- a/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java
@@ -22,7 +22,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
 
 import org.junit.Test;
 
diff --git a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
index bee270e..ba29a97 100644
--- a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
@@ -22,7 +22,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
 
 import org.junit.Test;
 
diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/os/TimestampedValueTest.java
similarity index 98%
rename from core/tests/coretests/src/android/util/TimestampedValueTest.java
rename to core/tests/coretests/src/android/os/TimestampedValueTest.java
index 6fc2400..f36d9e6 100644
--- a/core/tests/coretests/src/android/util/TimestampedValueTest.java
+++ b/core/tests/coretests/src/android/os/TimestampedValueTest.java
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-package android.util;
+package android.os;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.fail;
 
-import android.os.Parcel;
-
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 6062088..fa2ffcca 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -23,6 +23,7 @@
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 
 import static org.junit.Assert.assertEquals;
@@ -116,14 +117,18 @@
 
     @Test
     public void testCalculateInsets_imeIgnoredWithoutAdjustResize() {
-        mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
-        mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
-        mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
-        mState.getSource(ITYPE_IME).setVisible(true);
-        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
-                DisplayCutout.NO_CUTOUT, null, null, 0, null);
-        assertEquals(0, insets.getSystemWindowInsetBottom());
-        assertTrue(insets.isVisible(ime()));
+        try (final InsetsModeSession session =
+                     new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+            mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+            mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+            mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+            mState.getSource(ITYPE_IME).setVisible(true);
+            WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+                    DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_NOTHING, null);
+            assertEquals(0, insets.getSystemWindowInsetBottom());
+            assertEquals(100, insets.getInsets(ime()).bottom);
+            assertTrue(insets.isVisible(ime()));
+        }
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index 8c7b28a..e5a4f6d 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -63,7 +63,8 @@
         b.setInsets(navigationBars(), Insets.of(0, 0, 0, 100));
         b.setInsets(ime(), Insets.of(0, 0, 0, 300));
         WindowInsets insets = b.build();
-        assertEquals(300, insets.getSystemWindowInsets().bottom);
+        assertEquals(100, insets.getSystemWindowInsets().bottom);
+        assertEquals(300, insets.getInsets(ime()).bottom);
     }
 
     // TODO: Move this to CTS once API made public
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
similarity index 78%
rename from services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java
rename to core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index b9b6d55..e23c51e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.accessibility;
+package android.view.accessibility;
 
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertSame;
@@ -29,16 +29,17 @@
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Instrumentation;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
 import android.os.UserHandle;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IAccessibilityManager;
-import android.view.accessibility.IAccessibilityManagerClient;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.IntPair;
+import com.android.server.accessibility.test.MessageCapturingHandler;
 
 import org.junit.After;
 import org.junit.Before;
@@ -57,6 +58,16 @@
 public class AccessibilityManagerTest {
     private static final boolean WITH_A11Y_ENABLED = true;
     private static final boolean WITH_A11Y_DISABLED = false;
+    private static final String LABEL = "label";
+    private static final String INTENT_ACTION = "TESTACTION";
+    private static final String DESCRIPTION = "description";
+    private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast(
+            InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION), 0);
+    private static final RemoteAction TEST_ACTION = new RemoteAction(
+            Icon.createWithContentUri("content://test"),
+            LABEL,
+            DESCRIPTION,
+            TEST_PENDING_INTENT);
 
     @Mock private IAccessibilityManager mMockService;
     private MessageCapturingHandler mHandler;
@@ -122,6 +133,29 @@
     }
 
     @Test
+    public void testRegisterSystemAction() throws Exception {
+        AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+        RemoteAction action = new RemoteAction(
+                Icon.createWithContentUri("content://test"),
+                LABEL,
+                DESCRIPTION,
+                TEST_PENDING_INTENT);
+        final int actionId = 0;
+        manager.registerSystemAction(TEST_ACTION, actionId);
+
+        verify(mMockService).registerSystemAction(TEST_ACTION, actionId);
+    }
+
+    @Test
+    public void testUnregisterSystemAction() throws Exception {
+        AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+        final int actionId = 0;
+        manager.unregisterSystemAction(actionId);
+
+        verify(mMockService).unregisterSystemAction(actionId);
+    }
+
+    @Test
     public void testIsEnabled() throws Exception {
         // Create manager with a11y enabled
         AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
diff --git a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
index ffc925f..f108eb8 100644
--- a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
+++ b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
@@ -121,7 +121,12 @@
         AndroidFuture future2 = AndroidFuture.CREATOR.createFromParcel(parcel);
         ExecutionException executionException =
                 expectThrows(ExecutionException.class, future2::get);
-        assertThat(executionException.getCause()).isInstanceOf(UnsupportedOperationException.class);
+
+        Throwable cause = executionException.getCause();
+        String msg = cause.getMessage();
+        assertThat(cause).isInstanceOf(UnsupportedOperationException.class);
+        assertThat(msg).contains(getClass().getName());
+        assertThat(msg).contains("testWriteToParcel_Exception");
     }
 
     @Test
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 66d84aa..9018320 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -79,138 +79,6 @@
 
     @Test
     @UiThreadTest
-    public void testWindowVisibilityChange_OnCreate() throws Exception {
-        try (ClientMockSession clientSession = new ClientMockSession()) {
-            ActivityClientRecord r = clientSession.stubActivityRecord();
-
-            clientSession.launchActivity(r);
-            assertEquals(ON_CREATE, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, true);
-            assertEquals(ON_CREATE, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, false);
-            assertEquals(ON_CREATE, r.getLifecycleState());
-        }
-    }
-
-    @Test
-    @UiThreadTest
-    public void testWindowVisibilityChange_OnCreate_Finished() throws Exception {
-        try (ClientMockSession clientSession = new ClientMockSession()) {
-            ActivityClientRecord r = clientSession.stubActivityRecord();
-
-            Activity activity = clientSession.launchActivity(r);
-            activity.finish();
-            assertEquals(ON_CREATE, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, true);
-            assertEquals(ON_CREATE, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, false);
-            assertEquals(ON_CREATE, r.getLifecycleState());
-        }
-    }
-
-    @Test
-    @UiThreadTest
-    public void testWindowVisibilityChange_OnStart() throws Exception {
-        try (ClientMockSession clientSession = new ClientMockSession()) {
-            ActivityClientRecord r = clientSession.stubActivityRecord();
-
-            clientSession.launchActivity(r);
-            clientSession.startActivity(r);
-            assertEquals(ON_START, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, false);
-            assertEquals(ON_STOP, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, true);
-            assertEquals(ON_START, r.getLifecycleState());
-        }
-    }
-
-    @Test
-    @UiThreadTest
-    public void testWindowVisibilityChange_OnStart_Finished() throws Exception {
-        try (ClientMockSession clientSession = new ClientMockSession()) {
-            ActivityClientRecord r = clientSession.stubActivityRecord();
-
-            Activity activity = clientSession.launchActivity(r);
-            clientSession.startActivity(r);
-            activity.finish();
-            assertEquals(ON_START, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, false);
-            assertEquals(ON_STOP, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, true);
-            assertEquals(ON_START, r.getLifecycleState());
-        }
-    }
-
-    @Test
-    @UiThreadTest
-    public void testWindowVisibilityChange_OnResume() throws Exception {
-        try (ClientMockSession clientSession = new ClientMockSession()) {
-            ActivityClientRecord r = clientSession.stubActivityRecord();
-
-            clientSession.launchActivity(r);
-            clientSession.startActivity(r);
-            clientSession.resumeActivity(r);
-            assertEquals(ON_RESUME, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, false);
-            assertEquals(ON_STOP, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, true);
-            assertEquals(ON_START, r.getLifecycleState());
-        }
-    }
-
-    @Test
-    @UiThreadTest
-    public void testWindowVisibilityChange_OnPause() throws Exception {
-        try (ClientMockSession clientSession = new ClientMockSession()) {
-            ActivityClientRecord r = clientSession.stubActivityRecord();
-
-            clientSession.launchActivity(r);
-            clientSession.startActivity(r);
-            clientSession.resumeActivity(r);
-            clientSession.pauseActivity(r);
-            assertEquals(ON_PAUSE, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, false);
-            assertEquals(ON_STOP, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, true);
-            assertEquals(ON_START, r.getLifecycleState());
-        }
-    }
-
-    @Test
-    @UiThreadTest
-    public void testWindowVisibilityChange_OnStop() throws Exception {
-        try (ClientMockSession clientSession = new ClientMockSession()) {
-            ActivityClientRecord r = clientSession.stubActivityRecord();
-
-            clientSession.launchActivity(r);
-            clientSession.startActivity(r);
-            clientSession.resumeActivity(r);
-            clientSession.pauseActivity(r);
-            clientSession.stopActivity(r);
-            assertEquals(ON_STOP, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, true);
-            assertEquals(ON_START, r.getLifecycleState());
-
-            clientSession.changeVisibility(r, false);
-            assertEquals(ON_STOP, r.getLifecycleState());
-        }
-    }
-
-    @Test
-    @UiThreadTest
     public void testLifecycleAfterFinished_OnCreate() throws Exception {
         try (ClientMockSession clientSession = new ClientMockSession()) {
             ActivityClientRecord r = clientSession.stubActivityRecord();
@@ -308,7 +176,7 @@
         }
 
         private void startActivity(ActivityClientRecord r) {
-            mThread.handleStartActivity(r, null /* pendingActions */);
+            mThread.handleStartActivity(r.token, null /* pendingActions */);
         }
 
         private void resumeActivity(ActivityClientRecord r) {
@@ -323,7 +191,7 @@
         }
 
         private void stopActivity(ActivityClientRecord r) {
-            mThread.handleStopActivity(r.token, false /* show */, 0 /* configChanges */,
+            mThread.handleStopActivity(r.token, 0 /* configChanges */,
                     new PendingTransactionActions(), false /* finalStateRequest */, "test");
         }
 
@@ -332,10 +200,6 @@
                     false /* getNonConfigInstance */, "test");
         }
 
-        private void changeVisibility(ActivityClientRecord r, boolean show) {
-            mThread.handleWindowVisibility(r.token, show);
-        }
-
         private ActivityClientRecord stubActivityRecord() {
             ComponentName component = new ComponentName(
                     InstrumentationRegistry.getInstrumentation().getContext(), TestActivity.class);
diff --git a/core/xsd/permission.xsd b/core/xsd/permission.xsd
index cc01a31..5435047 100644
--- a/core/xsd/permission.xsd
+++ b/core/xsd/permission.xsd
@@ -46,6 +46,7 @@
                 <xs:element name="hidden-api-whitelisted-app" type="hidden-api-whitelisted-app"/>
                 <xs:element name="allow-association" type="allow-association"/>
                 <xs:element name="bugreport-whitelisted" type="bugreport-whitelisted"/>
+                <xs:element name="app-data-isolation-whitelisted-app" type="app-data-isolation-whitelisted-app"/>
             </xs:choice>
         </xs:complexType>
     </xs:element>
@@ -161,6 +162,9 @@
         <xs:attribute name="target" type="xs:string"/>
         <xs:attribute name="allowed" type="xs:string"/>
     </xs:complexType>
+    <xs:complexType name="app-data-isolation-whitelisted-app">
+        <xs:attribute name="package" type="xs:string"/>
+    </xs:complexType>
     <xs:complexType name="bugreport-whitelisted">
         <xs:attribute name="package" type="xs:string"/>
     </xs:complexType>
diff --git a/core/xsd/schema/current.txt b/core/xsd/schema/current.txt
index 771c1df..c36c422 100644
--- a/core/xsd/schema/current.txt
+++ b/core/xsd/schema/current.txt
@@ -45,6 +45,12 @@
     method public void set_package(String);
   }
 
+  public class AppDataIsolationWhitelistedApp {
+    ctor public AppDataIsolationWhitelistedApp();
+    method public String get_package();
+    method public void set_package(String);
+  }
+
   public class AppLink {
     ctor public AppLink();
     method public String get_package();
@@ -160,6 +166,7 @@
     method public java.util.List<com.android.xml.permission.configfile.AllowInPowerSaveExceptIdle> getAllowInPowerSaveExceptIdle_optional();
     method public java.util.List<com.android.xml.permission.configfile.AllowInPowerSave> getAllowInPowerSave_optional();
     method public java.util.List<com.android.xml.permission.configfile.AllowUnthrottledLocation> getAllowUnthrottledLocation_optional();
+    method public java.util.List<com.android.xml.permission.configfile.AppDataIsolationWhitelistedApp> getAppDataIsolationWhitelistedApp_optional();
     method public java.util.List<com.android.xml.permission.configfile.AppLink> getAppLink_optional();
     method public java.util.List<com.android.xml.permission.configfile.AssignPermission> getAssignPermission_optional();
     method public java.util.List<com.android.xml.permission.configfile.BackupTransportWhitelistedService> getBackupTransportWhitelistedService_optional();
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index e53f3db..6d4a0c6 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -52,8 +52,8 @@
     DeviceInfo::get()->mMaxTextureSize = maxTextureSize;
 }
 
-void DeviceInfo::onDisplayConfigChanged() {
-    updateDisplayInfo();
+void DeviceInfo::onRefreshRateChanged(int64_t vsyncPeriod) {
+    mVsyncPeriod = vsyncPeriod;
 }
 
 void DeviceInfo::updateDisplayInfo() {
@@ -113,10 +113,11 @@
     ADisplay* primaryDisplay = mDisplays[mPhysicalDisplayIndex];
     status_t status = ADisplay_getCurrentConfig(primaryDisplay, &mCurrentConfig);
     LOG_ALWAYS_FATAL_IF(status, "Failed to get display config, error %d", status);
+
     mWidth = ADisplayConfig_getWidth(mCurrentConfig);
     mHeight = ADisplayConfig_getHeight(mCurrentConfig);
     mDensity = ADisplayConfig_getDensity(mCurrentConfig);
-    mRefreshRate = ADisplayConfig_getFps(mCurrentConfig);
+    mVsyncPeriod = static_cast<int64_t>(1000000000 / ADisplayConfig_getFps(mCurrentConfig));
     mCompositorOffset = ADisplayConfig_getCompositorOffsetNanos(mCurrentConfig);
     mAppOffset = ADisplayConfig_getAppVsyncOffsetNanos(mCurrentConfig);
 }
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index a420746..16a22f4 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -37,7 +37,7 @@
     static int32_t getWidth() { return get()->mWidth; }
     static int32_t getHeight() { return get()->mHeight; }
     static float getDensity() { return get()->mDensity; }
-    static float getRefreshRate() { return get()->mRefreshRate; }
+    static int64_t getVsyncPeriod() { return get()->mVsyncPeriod; }
     static int64_t getCompositorOffset() { return get()->mCompositorOffset; }
     static int64_t getAppOffset() { return get()->mAppOffset; }
 
@@ -47,7 +47,8 @@
     sk_sp<SkColorSpace> getWideColorSpace() const { return mWideColorSpace; }
     SkColorType getWideColorType() const { return mWideColorType; }
 
-    void onDisplayConfigChanged();
+    // This method should be called whenever the display refresh rate changes.
+    void onRefreshRateChanged(int64_t vsyncPeriod);
 
 private:
     friend class renderthread::RenderThread;
@@ -68,7 +69,7 @@
     int32_t mWidth = 1080;
     int32_t mHeight = 1920;
     float mDensity = 2.0;
-    float mRefreshRate = 60.0;
+    int64_t mVsyncPeriod = 16666666;
     int64_t mCompositorOffset = 0;
     int64_t mAppOffset = 0;
 };
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 10e7160..d25fc4b 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -81,7 +81,7 @@
 
 JankTracker::JankTracker(ProfileDataContainer* globalData) {
     mGlobalData = globalData;
-    nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / DeviceInfo::getRefreshRate());
+    nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod();
     nsecs_t sfOffset = DeviceInfo::getCompositorOffset();
     nsecs_t offsetDelta = sfOffset - DeviceInfo::getAppOffset();
     // There are two different offset cases. If the offsetDelta is positive
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index f4149b9..84549e8 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -152,7 +152,9 @@
     AHardwareBuffer_describe(hardwareBuffer, &bufferDesc);
     SkImageInfo info = uirenderer::BufferDescriptionToImageInfo(bufferDesc, colorSpace);
 
-    const size_t rowBytes = info.bytesPerPixel() * bufferDesc.stride;
+    // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer)
+    const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width;
+    const size_t rowBytes = info.bytesPerPixel() * bufferStride;
     return sk_sp<Bitmap>(new Bitmap(hardwareBuffer, info, rowBytes, palette));
 }
 
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index a446858..d78f641 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -34,7 +34,6 @@
 #include <GrContextOptions.h>
 #include <gl/GrGLInterface.h>
 
-#include <gui/DisplayEventReceiver.h>
 #include <sys/resource.h>
 #include <utils/Condition.h>
 #include <utils/Log.h>
@@ -45,53 +44,43 @@
 namespace uirenderer {
 namespace renderthread {
 
-// Number of events to read at a time from the DisplayEventReceiver pipe.
-// The value should be large enough that we can quickly drain the pipe
-// using just a few large reads.
-static const size_t EVENT_BUFFER_SIZE = 100;
-
 static bool gHasRenderThreadInstance = false;
 
 static JVMAttachHook gOnStartHook = nullptr;
 
-class DisplayEventReceiverWrapper : public VsyncSource {
+void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
+    RenderThread* rt = reinterpret_cast<RenderThread*>(data);
+    rt->mVsyncRequested = false;
+    if (rt->timeLord().vsyncReceived(frameTimeNanos) && !rt->mFrameCallbackTaskPending) {
+        ATRACE_NAME("queue mFrameCallbackTask");
+        rt->mFrameCallbackTaskPending = true;
+        nsecs_t runAt = (frameTimeNanos + rt->mDispatchFrameDelay);
+        rt->queue().postAt(runAt, [=]() { rt->dispatchFrameCallbacks(); });
+    }
+}
+
+void RenderThread::refreshRateCallback(int64_t vsyncPeriod, void* data) {
+    ATRACE_NAME("refreshRateCallback");
+    RenderThread* rt = reinterpret_cast<RenderThread*>(data);
+    DeviceInfo::get()->onRefreshRateChanged(vsyncPeriod);
+    rt->setupFrameInterval();
+}
+
+class ChoreographerSource : public VsyncSource {
 public:
-    DisplayEventReceiverWrapper(std::unique_ptr<DisplayEventReceiver>&& receiver,
-            const std::function<void()>& onDisplayConfigChanged)
-            : mDisplayEventReceiver(std::move(receiver))
-            , mOnDisplayConfigChanged(onDisplayConfigChanged) {}
+    ChoreographerSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
 
     virtual void requestNextVsync() override {
-        status_t status = mDisplayEventReceiver->requestNextVsync();
-        LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "requestNextVsync failed with status: %d", status);
+        AChoreographer_postFrameCallback64(mRenderThread->mChoreographer,
+                                           RenderThread::frameCallback, mRenderThread);
     }
 
-    virtual nsecs_t latestVsyncEvent() override {
-        DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
-        nsecs_t latest = 0;
-        ssize_t n;
-        while ((n = mDisplayEventReceiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
-            for (ssize_t i = 0; i < n; i++) {
-                const DisplayEventReceiver::Event& ev = buf[i];
-                switch (ev.header.type) {
-                    case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
-                        latest = ev.header.timestamp;
-                        break;
-                    case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
-                        mOnDisplayConfigChanged();
-                        break;
-                }
-            }
-        }
-        if (n < 0) {
-            ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
-        }
-        return latest;
+    virtual void drainPendingEvents() override {
+        AChoreographer_handlePendingEvents(mRenderThread->mChoreographer, mRenderThread);
     }
 
 private:
-    std::unique_ptr<DisplayEventReceiver> mDisplayEventReceiver;
-    std::function<void()> mOnDisplayConfigChanged;
+    RenderThread* mRenderThread;
 };
 
 class DummyVsyncSource : public VsyncSource {
@@ -99,11 +88,14 @@
     DummyVsyncSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
 
     virtual void requestNextVsync() override {
-        mRenderThread->queue().postDelayed(16_ms,
-                                           [this]() { mRenderThread->drainDisplayEventQueue(); });
+        mRenderThread->queue().postDelayed(16_ms, [this]() {
+            RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread);
+        });
     }
 
-    virtual nsecs_t latestVsyncEvent() override { return systemTime(SYSTEM_TIME_MONOTONIC); }
+    virtual void drainPendingEvents() override {
+        RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread);
+    }
 
 private:
     RenderThread* mRenderThread;
@@ -145,29 +137,24 @@
 }
 
 RenderThread::~RenderThread() {
+    // Note that if this fatal assertion is removed then member variables must
+    // be properly destroyed.
     LOG_ALWAYS_FATAL("Can't destroy the render thread");
 }
 
-void RenderThread::initializeDisplayEventReceiver() {
-    LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second DisplayEventReceiver?");
+void RenderThread::initializeChoreographer() {
+    LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second Choreographer?");
 
     if (!Properties::isolatedProcess) {
-        auto receiver = std::make_unique<DisplayEventReceiver>(
-            ISurfaceComposer::eVsyncSourceApp,
-            ISurfaceComposer::eConfigChangedDispatch);
-        status_t status = receiver->initCheck();
-        LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
-                            "Initialization of DisplayEventReceiver "
-                            "failed with status: %d",
-                            status);
+        mChoreographer = AChoreographer_create();
+        LOG_ALWAYS_FATAL_IF(mChoreographer == nullptr, "Initialization of Choreographer failed");
+        AChoreographer_registerRefreshRateCallback(mChoreographer,
+                                                   RenderThread::refreshRateCallback, this);
 
         // Register the FD
-        mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT,
-                       RenderThread::displayEventReceiverCallback, this);
-        mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver), [this] {
-            DeviceInfo::get()->onDisplayConfigChanged();
-            setupFrameInterval();
-        });
+        mLooper->addFd(AChoreographer_getFd(mChoreographer), 0, Looper::EVENT_INPUT,
+                       RenderThread::choreographerCallback, this);
+        mVsyncSource = new ChoreographerSource(this);
     } else {
         mVsyncSource = new DummyVsyncSource(this);
     }
@@ -175,7 +162,7 @@
 
 void RenderThread::initThreadLocals() {
     setupFrameInterval();
-    initializeDisplayEventReceiver();
+    initializeChoreographer();
     mEglManager = new EglManager();
     mRenderState = new RenderState(*this);
     mVkManager = new VulkanManager();
@@ -183,7 +170,7 @@
 }
 
 void RenderThread::setupFrameInterval() {
-    nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / DeviceInfo::getRefreshRate());
+    nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod();
     mTimeLord.setFrameInterval(frameIntervalNanos);
     mDispatchFrameDelay = static_cast<nsecs_t>(frameIntervalNanos * .25f);
 }
@@ -288,7 +275,7 @@
     }
 }
 
-int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
+int RenderThread::choreographerCallback(int fd, int events, void* data) {
     if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
         ALOGE("Display event receiver pipe was closed or an error occurred.  "
               "events=0x%x",
@@ -302,24 +289,10 @@
               events);
         return 1;  // keep the callback
     }
+    RenderThread* rt = reinterpret_cast<RenderThread*>(data);
+    AChoreographer_handlePendingEvents(rt->mChoreographer, data);
 
-    reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue();
-
-    return 1;  // keep the callback
-}
-
-void RenderThread::drainDisplayEventQueue() {
-    ATRACE_CALL();
-    nsecs_t vsyncEvent = mVsyncSource->latestVsyncEvent();
-    if (vsyncEvent > 0) {
-        mVsyncRequested = false;
-        if (mTimeLord.vsyncReceived(vsyncEvent) && !mFrameCallbackTaskPending) {
-            ATRACE_NAME("queue mFrameCallbackTask");
-            mFrameCallbackTaskPending = true;
-            nsecs_t runAt = (vsyncEvent + mDispatchFrameDelay);
-            queue().postAt(runAt, [this]() { dispatchFrameCallbacks(); });
-        }
-    }
+    return 1;
 }
 
 void RenderThread::dispatchFrameCallbacks() {
@@ -360,7 +333,7 @@
         processQueue();
 
         if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
-            drainDisplayEventQueue();
+            mVsyncSource->drainPendingEvents();
             mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(),
                                    mPendingRegistrationFrameCallbacks.end());
             mPendingRegistrationFrameCallbacks.clear();
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index da79e97..8be46a6 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -19,6 +19,7 @@
 
 #include <GrContext.h>
 #include <SkBitmap.h>
+#include <apex/choreographer.h>
 #include <cutils/compiler.h>
 #include <thread/ThreadBase.h>
 #include <utils/Looper.h>
@@ -73,10 +74,11 @@
 
 struct VsyncSource {
     virtual void requestNextVsync() = 0;
-    virtual nsecs_t latestVsyncEvent() = 0;
+    virtual void drainPendingEvents() = 0;
     virtual ~VsyncSource() {}
 };
 
+class ChoreographerSource;
 class DummyVsyncSource;
 
 typedef void (*JVMAttachHook)(const char* name);
@@ -136,6 +138,7 @@
     friend class DispatchFrameCallbacks;
     friend class RenderProxy;
     friend class DummyVsyncSource;
+    friend class ChoreographerSource;
     friend class android::uirenderer::AutoBackendTextureRelease;
     friend class android::uirenderer::TestUtils;
     friend class android::uirenderer::WebViewFunctor;
@@ -149,13 +152,21 @@
     static RenderThread& getInstance();
 
     void initThreadLocals();
-    void initializeDisplayEventReceiver();
+    void initializeChoreographer();
     void setupFrameInterval();
-    static int displayEventReceiverCallback(int fd, int events, void* data);
+    // Callbacks for choreographer events:
+    // choreographerCallback will call AChoreograper_handleEvent to call the
+    // corresponding callbacks for each display event type
+    static int choreographerCallback(int fd, int events, void* data);
+    // Callback that will be run on vsync ticks.
+    static void frameCallback(int64_t frameTimeNanos, void* data);
+    // Callback that will be run whenver there is a refresh rate change.
+    static void refreshRateCallback(int64_t vsyncPeriod, void* data);
     void drainDisplayEventQueue();
     void dispatchFrameCallbacks();
     void requestVsync();
 
+    AChoreographer* mChoreographer;
     VsyncSource* mVsyncSource;
     bool mVsyncRequested;
     std::set<IFrameCallback*> mFrameCallbacks;
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index 02a3816..28bf84d 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -24,13 +24,12 @@
  */
 oneway interface IMediaRoute2Provider {
     void setClient(IMediaRoute2ProviderClient client);
-    void requestCreateSession(String packageName, String routeId,
-            String controlCategory, long requestId);
-    void releaseSession(int sessionId);
+    void requestCreateSession(String packageName, String routeId, String routeType, long requestId);
+    void releaseSession(String sessionId);
 
-    void selectRoute(int sessionId, String routeId);
-    void deselectRoute(int sessionId, String routeId);
-    void transferToRoute(int sessionId, String routeId);
+    void selectRoute(String sessionId, String routeId);
+    void deselectRoute(String sessionId, String routeId);
+    void transferToRoute(String sessionId, String routeId);
 
     void notifyControlRequestSent(String id, in Intent request);
     void requestSetVolume(String id, int volume);
diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl
index b7cb705..e8af21e 100644
--- a/media/java/android/media/IMediaRouter2Manager.aidl
+++ b/media/java/android/media/IMediaRouter2Manager.aidl
@@ -24,7 +24,7 @@
  */
 oneway interface IMediaRouter2Manager {
     void notifyRouteSelected(String packageName, in MediaRoute2Info route);
-    void notifyControlCategoriesChanged(String packageName, in List<String> categories);
+    void notifyRouteTypesChanged(String packageName, in List<String> routeTypes);
     void notifyRoutesAdded(in List<MediaRoute2Info> routes);
     void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
     void notifyRoutesChanged(in List<MediaRoute2Info> routes);
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index e5b62ff..b573f64 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -22,6 +22,7 @@
 import android.media.IMediaRouterClient;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouterClientState;
+import android.media.RouteDiscoveryRequest;
 import android.media.RouteSessionInfo;
 
 /**
@@ -51,8 +52,8 @@
     void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
 
     void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route,
-            String controlCategory, int requestId);
-    void setControlCategories(IMediaRouter2Client client, in List<String> categories);
+            String routeType, int requestId);
+    void setDiscoveryRequest2(IMediaRouter2Client client, in RouteDiscoveryRequest request);
     void selectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
     void deselectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
     void transferToRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 7fca03c..4cd581b 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -354,8 +355,8 @@
      *         is less than or equal to 0.
      * @see {@link #getScaledFrameAtTime(long, int, int, int, BitmapParams)}
      */
-    public @Nullable Bitmap getScaledFrameAtTime(
-            long timeUs, @Option int option, int dstWidth, int dstHeight) {
+    public @Nullable Bitmap getScaledFrameAtTime(long timeUs, @Option int option,
+            @IntRange(from=1) int dstWidth, @IntRange(from=1) int dstHeight) {
         validate(option, dstWidth, dstHeight);
         return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, null);
     }
@@ -400,7 +401,8 @@
      * @see {@link #getScaledFrameAtTime(long, int, int, int)}
      */
     public @Nullable Bitmap getScaledFrameAtTime(long timeUs, @Option int option,
-            int dstWidth, int dstHeight, @NonNull BitmapParams params) {
+            @IntRange(from=1) int dstWidth, @IntRange(from=1) int dstHeight,
+            @NonNull BitmapParams params) {
         validate(option, dstWidth, dstHeight);
         return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, params);
     }
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 506d616..1ed53d9 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import static android.media.MediaRouter2Utils.toUniqueId;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -83,12 +85,14 @@
      * controlled from this object. An example of fixed playback volume is a remote player,
      * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
      * than attenuate at the source.
+     *
      * @see #getVolumeHandling()
      */
     public static final int PLAYBACK_VOLUME_FIXED = 0;
     /**
      * Playback information indicating the playback volume is variable and can be controlled
      * from this object.
+     *
      * @see #getVolumeHandling()
      */
     public static final int PLAYBACK_VOLUME_VARIABLE = 1;
@@ -148,7 +152,7 @@
     @Nullable
     final String mClientPackageName;
     @NonNull
-    final List<String> mSupportedCategories;
+    final List<String> mRouteTypes;
     final int mVolume;
     final int mVolumeMax;
     final int mVolumeHandling;
@@ -156,8 +160,6 @@
     @Nullable
     final Bundle mExtras;
 
-    private final String mUniqueId;
-
     MediaRoute2Info(@NonNull Builder builder) {
         mId = builder.mId;
         mProviderId = builder.mProviderId;
@@ -166,13 +168,12 @@
         mConnectionState = builder.mConnectionState;
         mIconUri = builder.mIconUri;
         mClientPackageName = builder.mClientPackageName;
-        mSupportedCategories = builder.mSupportedCategories;
+        mRouteTypes = builder.mRouteTypes;
         mVolume = builder.mVolume;
         mVolumeMax = builder.mVolumeMax;
         mVolumeHandling = builder.mVolumeHandling;
         mDeviceType = builder.mDeviceType;
         mExtras = builder.mExtras;
-        mUniqueId = createUniqueId();
     }
 
     MediaRoute2Info(@NonNull Parcel in) {
@@ -183,25 +184,12 @@
         mConnectionState = in.readInt();
         mIconUri = in.readParcelable(null);
         mClientPackageName = in.readString();
-        mSupportedCategories = in.createStringArrayList();
+        mRouteTypes = in.createStringArrayList();
         mVolume = in.readInt();
         mVolumeMax = in.readInt();
         mVolumeHandling = in.readInt();
         mDeviceType = in.readInt();
         mExtras = in.readBundle();
-        mUniqueId = createUniqueId();
-    }
-
-    private String createUniqueId() {
-        String uniqueId = null;
-        if (mProviderId != null) {
-            uniqueId = toUniqueId(mProviderId, mId);
-        }
-        return uniqueId;
-    }
-
-    static String toUniqueId(String providerId, String routeId) {
-        return providerId + ":" + routeId;
     }
 
     /**
@@ -235,7 +223,7 @@
                 && (mConnectionState == other.mConnectionState)
                 && Objects.equals(mIconUri, other.mIconUri)
                 && Objects.equals(mClientPackageName, other.mClientPackageName)
-                && Objects.equals(mSupportedCategories, other.mSupportedCategories)
+                && Objects.equals(mRouteTypes, other.mRouteTypes)
                 && (mVolume == other.mVolume)
                 && (mVolumeMax == other.mVolumeMax)
                 && (mVolumeHandling == other.mVolumeHandling)
@@ -247,29 +235,34 @@
     @Override
     public int hashCode() {
         return Objects.hash(mId, mName, mDescription, mConnectionState, mIconUri,
-                mSupportedCategories, mVolume, mVolumeMax, mVolumeHandling, mDeviceType);
+                mRouteTypes, mVolume, mVolumeMax, mVolumeHandling, mDeviceType);
     }
 
     /**
-     * Gets the id of the route.
-     * Use {@link #getUniqueId()} if you need a unique identifier.
+     * Gets the id of the route. The routes which are given by {@link MediaRouter2} will have
+     * unique IDs.
+     * <p>
+     * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method
+     * can be different from what was set in {@link MediaRoute2ProviderService}.
      *
-     * @see #getUniqueId()
+     * @see Builder#setId(String)
      */
     @NonNull
     public String getId() {
-        return mId;
+        if (mProviderId != null) {
+            return toUniqueId(mProviderId, mId);
+        } else {
+            return mId;
+        }
     }
 
     /**
-     * Gets the unique id of the route. A route obtained from
-     * {@link com.android.server.media.MediaRouterService} always has a unique id.
-     *
-     * @return unique id of the route or null if it has no unique id.
+     * Gets the original id set by {@link Builder#setId(String)}.
+     * @hide
      */
-    @Nullable
-    public String getUniqueId() {
-        return mUniqueId;
+    @NonNull
+    public String getOriginalId() {
+        return mId;
     }
 
     /**
@@ -331,8 +324,8 @@
      * Gets the supported categories of the route.
      */
     @NonNull
-    public List<String> getSupportedCategories() {
-        return mSupportedCategories;
+    public List<String> getRouteTypes() {
+        return mRouteTypes;
     }
 
     //TODO: once device types are confirmed, reflect those into the comment.
@@ -376,32 +369,15 @@
     }
 
     /**
-     * Returns if the route supports the specified control category
+     * Returns if the route contains at least one of the specified route types.
      *
-     * @param controlCategory control category to consider
-     * @return true if the route supports at the category
+     * @param routeTypes the list of route types to consider
+     * @return true if the route contains at least one type in the list
      */
-    public boolean supportsControlCategory(@NonNull String controlCategory) {
-        Objects.requireNonNull(controlCategory, "control category must not be null");
-        for (String supportedCategory : getSupportedCategories()) {
-            if (TextUtils.equals(controlCategory, supportedCategory)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    //TODO: Move this if we re-define control category / selector things.
-    /**
-     * Returns if the route supports at least one of the specified control categories
-     *
-     * @param controlCategories the list of control categories to consider
-     * @return true if the route supports at least one category
-     */
-    public boolean supportsControlCategories(@NonNull Collection<String> controlCategories) {
-        Objects.requireNonNull(controlCategories, "control categories must not be null");
-        for (String controlCategory : controlCategories) {
-            if (supportsControlCategory(controlCategory)) {
+    public boolean containsRouteTypes(@NonNull Collection<String> routeTypes) {
+        Objects.requireNonNull(routeTypes, "routeTypes must not be null");
+        for (String routeType : routeTypes) {
+            if (getRouteTypes().contains(routeType)) {
                 return true;
             }
         }
@@ -422,7 +398,7 @@
         dest.writeInt(mConnectionState);
         dest.writeParcelable(mIconUri, flags);
         dest.writeString(mClientPackageName);
-        dest.writeStringList(mSupportedCategories);
+        dest.writeStringList(mRouteTypes);
         dest.writeInt(mVolume);
         dest.writeInt(mVolumeMax);
         dest.writeInt(mVolumeHandling);
@@ -460,7 +436,7 @@
         int mConnectionState;
         Uri mIconUri;
         String mClientPackageName;
-        List<String> mSupportedCategories;
+        List<String> mRouteTypes;
         int mVolume;
         int mVolumeMax;
         int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
@@ -471,7 +447,7 @@
         public Builder(@NonNull String id, @NonNull CharSequence name) {
             setId(id);
             setName(name);
-            mSupportedCategories = new ArrayList<>();
+            mRouteTypes = new ArrayList<>();
         }
 
         public Builder(@NonNull MediaRoute2Info routeInfo) {
@@ -488,7 +464,7 @@
             mConnectionState = routeInfo.mConnectionState;
             mIconUri = routeInfo.mIconUri;
             setClientPackageName(routeInfo.mClientPackageName);
-            setSupportedCategories(routeInfo.mSupportedCategories);
+            setRouteTypes(routeInfo.mRouteTypes);
             setVolume(routeInfo.mVolume);
             setVolumeMax(routeInfo.mVolumeMax);
             setVolumeHandling(routeInfo.mVolumeHandling);
@@ -499,7 +475,15 @@
         }
 
         /**
-         * Sets the unique id of the route.
+         * Sets the unique id of the route. The value given here must be unique for each of your
+         * route.
+         * <p>
+         * In order to ensure uniqueness in {@link MediaRouter2} side, the value of
+         * {@link MediaRoute2Info#getId()} can be different from what was set in
+         * {@link MediaRoute2ProviderService}.
+         * </p>
+         *
+         * @see MediaRoute2Info#getId()
          */
         @NonNull
         public Builder setId(@NonNull String id) {
@@ -585,35 +569,35 @@
         }
 
         /**
-         * Sets the supported categories of the route.
+         * Sets the types of the route.
          */
         @NonNull
-        public Builder setSupportedCategories(@NonNull Collection<String> categories) {
-            mSupportedCategories = new ArrayList<>();
-            return addSupportedCategories(categories);
+        public Builder setRouteTypes(@NonNull Collection<String> routeTypes) {
+            mRouteTypes = new ArrayList<>();
+            return addRouteTypes(routeTypes);
         }
 
         /**
-         * Adds supported categories for the route.
+         * Adds types for the route.
          */
         @NonNull
-        public Builder addSupportedCategories(@NonNull Collection<String> categories) {
-            Objects.requireNonNull(categories, "categories must not be null");
-            for (String category: categories) {
-                addSupportedCategory(category);
+        public Builder addRouteTypes(@NonNull Collection<String> routeTypes) {
+            Objects.requireNonNull(routeTypes, "routeTypes must not be null");
+            for (String routeType: routeTypes) {
+                addRouteType(routeType);
             }
             return this;
         }
 
         /**
-         * Add a supported category for the route.
+         * Add a type for the route.
          */
         @NonNull
-        public Builder addSupportedCategory(@NonNull String category) {
-            if (TextUtils.isEmpty(category)) {
-                throw new IllegalArgumentException("category must not be null or empty");
+        public Builder addRouteType(@NonNull String routeType) {
+            if (TextUtils.isEmpty(routeType)) {
+                throw new IllegalArgumentException("routeType must not be null or empty");
             }
-            mSupportedCategories.add(category);
+            mRouteTypes.add(routeType);
             return this;
         }
 
diff --git a/media/java/android/media/MediaRoute2ProviderInfo.java b/media/java/android/media/MediaRoute2ProviderInfo.java
index 7078d4a..e2f246c 100644
--- a/media/java/android/media/MediaRoute2ProviderInfo.java
+++ b/media/java/android/media/MediaRoute2ProviderInfo.java
@@ -25,6 +25,7 @@
 
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -161,14 +162,17 @@
                 return this;
             }
             mUniqueId = uniqueId;
-            final int count = mRoutes.size();
-            for (int i = 0; i < count; i++) {
-                MediaRoute2Info route = mRoutes.valueAt(i);
-                mRoutes.setValueAt(i, new MediaRoute2Info.Builder(route)
+
+            final ArrayMap<String, MediaRoute2Info> newRoutes = new ArrayMap<>();
+            for (Map.Entry<String, MediaRoute2Info> entry : mRoutes.entrySet()) {
+                MediaRoute2Info routeWithProviderId = new MediaRoute2Info.Builder(entry.getValue())
                         .setProviderId(mUniqueId)
-                        .build());
+                        .build();
+                newRoutes.put(routeWithProviderId.getId(), routeWithProviderId);
             }
 
+            mRoutes.clear();
+            mRoutes.putAll(newRoutes);
             return this;
         }
 
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 99bd1dc..24b65ba 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -29,6 +29,7 @@
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -55,7 +56,7 @@
     private MediaRoute2ProviderInfo mProviderInfo;
 
     @GuardedBy("mSessionLock")
-    private ArrayMap<Integer, RouteSessionInfo> mSessionInfo = new ArrayMap<>();
+    private ArrayMap<String, RouteSessionInfo> mSessionInfo = new ArrayMap<>();
 
     public MediaRoute2ProviderService() {
         mHandler = new Handler(Looper.getMainLooper());
@@ -106,7 +107,10 @@
      *         null if the session is destroyed or id is not valid.
      */
     @Nullable
-    public final RouteSessionInfo getSessionInfo(int sessionId) {
+    public final RouteSessionInfo getSessionInfo(@NonNull String sessionId) {
+        if (TextUtils.isEmpty(sessionId)) {
+            throw new IllegalArgumentException("sessionId must not be empty");
+        }
         synchronized (mSessionLock) {
             return mSessionInfo.get(sessionId);
         }
@@ -134,7 +138,7 @@
      */
     public final void updateSessionInfo(@NonNull RouteSessionInfo sessionInfo) {
         Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
-        int sessionId = sessionInfo.getSessionId();
+        String sessionId = sessionInfo.getId();
         if (sessionInfo.getSelectedRoutes().isEmpty()) {
             releaseSession(sessionId);
             return;
@@ -160,7 +164,7 @@
     public final void notifySessionInfoChanged(@NonNull RouteSessionInfo sessionInfo) {
         Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
 
-        int sessionId = sessionInfo.getSessionId();
+        String sessionId = sessionInfo.getId();
         synchronized (mSessionLock) {
             if (mSessionInfo.containsKey(sessionId)) {
                 mSessionInfo.put(sessionId, sessionInfo);
@@ -185,7 +189,7 @@
      * controlled, pass a {@link Bundle} that contains how to control it.
      *
      * @param sessionInfo information of the new session.
-     *                    The {@link RouteSessionInfo#getSessionId() id} of the session must be
+     *                    The {@link RouteSessionInfo#getId() id} of the session must be
      *                    unique. Pass {@code null} to reject the request or inform clients that
      *                    session creation is failed.
      * @param requestId id of the previous request to create this session
@@ -194,13 +198,13 @@
     // TODO: Maybe better to create notifySessionCreationFailed?
     public final void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) {
         if (sessionInfo != null) {
-            int sessionId = sessionInfo.getSessionId();
+            String sessionId = sessionInfo.getId();
             synchronized (mSessionLock) {
                 if (mSessionInfo.containsKey(sessionId)) {
                     Log.w(TAG, "Ignoring duplicate session id.");
                     return;
                 }
-                mSessionInfo.put(sessionInfo.getSessionId(), sessionInfo);
+                mSessionInfo.put(sessionInfo.getId(), sessionInfo);
             }
             schedulePublishState();
         }
@@ -220,9 +224,12 @@
      * {@link #onDestroySession} is called if the session is released.
      *
      * @param sessionId id of the session to be released
-     * @see #onDestroySession(int, RouteSessionInfo)
+     * @see #onDestroySession(String, RouteSessionInfo)
      */
-    public final void releaseSession(int sessionId) {
+    public final void releaseSession(@NonNull String sessionId) {
+        if (TextUtils.isEmpty(sessionId)) {
+            throw new IllegalArgumentException("sessionId must not be empty");
+        }
         //TODO: notify media router service of release.
         RouteSessionInfo sessionInfo;
         synchronized (mSessionLock) {
@@ -246,11 +253,11 @@
      *
      * @param packageName the package name of the application that selected the route
      * @param routeId the id of the route initially being connected
-     * @param controlCategory the control category of the new session
+     * @param routeType the route type of the new session
      * @param requestId the id of this session creation request
      */
     public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId,
-            @NonNull String controlCategory, long requestId);
+            @NonNull String routeType, long requestId);
 
     /**
      * Called when a session is about to be destroyed.
@@ -259,9 +266,10 @@
      *
      * @param sessionId id of the session being destroyed.
      * @param lastSessionInfo information of the session being destroyed.
-     * @see #releaseSession(int)
+     * @see #releaseSession(String)
      */
-    public abstract void onDestroySession(int sessionId, @NonNull RouteSessionInfo lastSessionInfo);
+    public abstract void onDestroySession(@NonNull String sessionId,
+            @NonNull RouteSessionInfo lastSessionInfo);
 
     //TODO: make a way to reject the request
     /**
@@ -274,7 +282,7 @@
      * @param routeId id of the route
      * @see #updateSessionInfo(RouteSessionInfo)
      */
-    public abstract void onSelectRoute(int sessionId, @NonNull String routeId);
+    public abstract void onSelectRoute(@NonNull String sessionId, @NonNull String routeId);
 
     //TODO: make a way to reject the request
     /**
@@ -286,7 +294,7 @@
      * @param sessionId id of the session
      * @param routeId id of the route
      */
-    public abstract void onDeselectRoute(int sessionId, @NonNull String routeId);
+    public abstract void onDeselectRoute(@NonNull String sessionId, @NonNull String routeId);
 
     //TODO: make a way to reject the request
     /**
@@ -298,7 +306,26 @@
      * @param sessionId id of the session
      * @param routeId id of the route
      */
-    public abstract void onTransferToRoute(int sessionId, @NonNull String routeId);
+    public abstract void onTransferToRoute(@NonNull String sessionId, @NonNull String routeId);
+
+    /**
+     * Called when the {@link RouteDiscoveryRequest discovery request} has changed.
+     * <p>
+     * Whenever an application registers a {@link MediaRouter2.RouteCallback callback},
+     * it also provides a discovery request to specify types of routes that it is interested in.
+     * The media router combines all of these discovery request into a single discovery request
+     * and notifies each provider.
+     * </p><p>
+     * The provider should examine {@link RouteDiscoveryRequest#getRouteTypes() route types}
+     * in the discovery request to determine what kind of routes it should try to discover
+     * and whether it should perform active or passive scans. In many cases, the provider may be
+     * able to save power by not performing any scans when the request doesn't have any matching
+     * route types.
+     * </p>
+     *
+     * @param request the new discovery request
+     */
+    public void onDiscoveryRequestChanged(@NonNull RouteDiscoveryRequest request) {}
 
     /**
      * Updates provider info and publishes routes and session info.
@@ -357,46 +384,62 @@
 
         @Override
         public void requestCreateSession(String packageName, String routeId,
-                String controlCategory, long requestId) {
+                String routeType, long requestId) {
             if (!checkCallerisSystem()) {
                 return;
             }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
-                    MediaRoute2ProviderService.this, packageName, routeId, controlCategory,
+                    MediaRoute2ProviderService.this, packageName, routeId, routeType,
                     requestId));
         }
         @Override
-        public void releaseSession(int sessionId) {
+        public void releaseSession(@NonNull String sessionId) {
             if (!checkCallerisSystem()) {
                 return;
             }
+            if (TextUtils.isEmpty(sessionId)) {
+                Log.w(TAG, "releaseSession: Ignoring empty sessionId from system service.");
+                return;
+            }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::releaseSession,
                     MediaRoute2ProviderService.this, sessionId));
         }
 
         @Override
-        public void selectRoute(int sessionId, String routeId) {
+        public void selectRoute(@NonNull String sessionId, String routeId) {
             if (!checkCallerisSystem()) {
                 return;
             }
+            if (TextUtils.isEmpty(sessionId)) {
+                Log.w(TAG, "selectRoute: Ignoring empty sessionId from system service.");
+                return;
+            }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute,
                     MediaRoute2ProviderService.this, sessionId, routeId));
         }
 
         @Override
-        public void deselectRoute(int sessionId, String routeId) {
+        public void deselectRoute(@NonNull String sessionId, String routeId) {
             if (!checkCallerisSystem()) {
                 return;
             }
+            if (TextUtils.isEmpty(sessionId)) {
+                Log.w(TAG, "deselectRoute: Ignoring empty sessionId from system service.");
+                return;
+            }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onDeselectRoute,
                     MediaRoute2ProviderService.this, sessionId, routeId));
         }
 
         @Override
-        public void transferToRoute(int sessionId, String routeId) {
+        public void transferToRoute(@NonNull String sessionId, String routeId) {
             if (!checkCallerisSystem()) {
                 return;
             }
+            if (TextUtils.isEmpty(sessionId)) {
+                Log.w(TAG, "transferToRoute: Ignoring empty sessionId from system service.");
+                return;
+            }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onTransferToRoute,
                     MediaRoute2ProviderService.this, sessionId, routeId));
         }
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index f5cfde4b..8ebf617 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -39,7 +39,6 @@
 
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -48,6 +47,7 @@
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 
 /**
  * A new Media Router
@@ -118,7 +118,7 @@
     final Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
 
     @GuardedBy("sLock")
-    private List<String> mControlCategories = Collections.emptyList();
+    private RouteDiscoveryRequest mDiscoveryRequest = RouteDiscoveryRequest.EMPTY;
 
     // TODO: Make MediaRouter2 is always connected to the MediaRouterService.
     @GuardedBy("sLock")
@@ -152,7 +152,6 @@
         mMediaRouterService = IMediaRouterService.Stub.asInterface(
                 ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
         mPackageName = mContext.getPackageName();
-        //TODO: read control categories from the manifest
         mHandler = new Handler(Looper.getMainLooper());
 
         List<MediaRoute2Info> currentSystemRoutes = null;
@@ -177,9 +176,9 @@
      * @hide
      */
     public static boolean checkRouteListContainsRouteId(@NonNull List<MediaRoute2Info> routeList,
-            @NonNull String uniqueRouteId) {
+            @NonNull String routeId) {
         for (MediaRoute2Info info : routeList) {
-            if (TextUtils.equals(uniqueRouteId, info.getUniqueId())) {
+            if (TextUtils.equals(routeId, info.getId())) {
                 return true;
             }
         }
@@ -188,24 +187,18 @@
 
     /**
      * Registers a callback to discover routes and to receive events when they change.
-     */
-    public void registerRouteCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull RouteCallback routeCallback) {
-        registerRouteCallback(executor, routeCallback, 0);
-    }
-
-    /**
-     * Registers a callback to discover routes and to receive events when they change.
      * <p>
      * If you register the same callback twice or more, it will be ignored.
      * </p>
      */
     public void registerRouteCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull RouteCallback routeCallback, int flags) {
+            @NonNull RouteCallback routeCallback,
+            @NonNull RouteDiscoveryRequest request) {
         Objects.requireNonNull(executor, "executor must not be null");
         Objects.requireNonNull(routeCallback, "callback must not be null");
+        Objects.requireNonNull(request, "request must not be null");
 
-        RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, flags);
+        RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, request);
         if (!mRouteCallbackRecords.addIfAbsent(record)) {
             Log.w(TAG, "Ignoring the same callback");
             return;
@@ -216,7 +209,8 @@
                 Client2 client = new Client2();
                 try {
                     mMediaRouterService.registerClient2(client, mPackageName);
-                    mMediaRouterService.setControlCategories(client, mControlCategories);
+                    updateDiscoveryRequestLocked();
+                    mMediaRouterService.setDiscoveryRequest2(client, mDiscoveryRequest);
                     mClient = client;
                 } catch (RemoteException ex) {
                     Log.e(TAG, "Unable to register media router.", ex);
@@ -238,7 +232,7 @@
         Objects.requireNonNull(routeCallback, "callback must not be null");
 
         if (!mRouteCallbackRecords.remove(
-                new RouteCallbackRecord(null, routeCallback, 0))) {
+                new RouteCallbackRecord(null, routeCallback, null))) {
             Log.w(TAG, "Ignoring unknown callback");
             return;
         }
@@ -256,30 +250,10 @@
         }
     }
 
-    //TODO(b/139033746): Rename "Control Category" when it's finalized.
-    /**
-     * Sets the control categories of the application.
-     * Routes that support at least one of the given control categories are handled
-     * by the media router.
-     */
-    public void setControlCategories(@NonNull Collection<String> controlCategories) {
-        Objects.requireNonNull(controlCategories, "control categories must not be null");
-
-        List<String> newControlCategories = new ArrayList<>(controlCategories);
-
-        synchronized (sRouterLock) {
-            mShouldUpdateRoutes = true;
-
-            // invoke callbacks due to control categories change
-            handleControlCategoriesChangedLocked(newControlCategories);
-            if (mClient != null) {
-                try {
-                    mMediaRouterService.setControlCategories(mClient, mControlCategories);
-                } catch (RemoteException ex) {
-                    Log.e(TAG, "Unable to set control categories.", ex);
-                }
-            }
-        }
+    private void updateDiscoveryRequestLocked() {
+        mDiscoveryRequest = new RouteDiscoveryRequest.Builder(
+                mRouteCallbackRecords.stream().map(record -> record.mRequest).collect(
+                        Collectors.toList())).build();
     }
 
     /**
@@ -287,8 +261,8 @@
      * known to the media router.
      * Please note that the list can be changed before callbacks are invoked.
      *
-     * @return the list of routes that support at least one of the control categories set by
-     * the application
+     * @return the list of routes that contains at least one of the route types in discovery
+     * requests registered by the application
      */
     @NonNull
     public List<MediaRoute2Info> getRoutes() {
@@ -298,7 +272,7 @@
 
                 List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
                 for (MediaRoute2Info route : mRoutes.values()) {
-                    if (route.supportsControlCategories(mControlCategories)) {
+                    if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) {
                         filteredRoutes.add(route);
                     }
                 }
@@ -350,26 +324,25 @@
      * Requests the media route provider service to create a session with the given route.
      *
      * @param route the route you want to create a session with.
-     * @param controlCategory the control category of the session. Should not be empty
+     * @param routeType the route type of the session. Should not be empty
      *
      * @see SessionCallback#onSessionCreated
      * @see SessionCallback#onSessionCreationFailed
      */
     @NonNull
     public void requestCreateSession(@NonNull MediaRoute2Info route,
-            @NonNull String controlCategory) {
+            @NonNull String routeType) {
         Objects.requireNonNull(route, "route must not be null");
-        if (TextUtils.isEmpty(controlCategory)) {
-            throw new IllegalArgumentException("controlCategory must not be empty");
+        if (TextUtils.isEmpty(routeType)) {
+            throw new IllegalArgumentException("routeType must not be empty");
         }
         // TODO: Check the given route exists
-        // TODO: Check the route supports the given controlCategory
+        // TODO: Check the route supports the given routeType
 
         final int requestId;
         requestId = mSessionCreationRequestCnt.getAndIncrement();
 
-        SessionCreationRequest request = new SessionCreationRequest(
-                requestId, route, controlCategory);
+        SessionCreationRequest request = new SessionCreationRequest(requestId, route, routeType);
         mSessionCreationRequests.add(request);
 
         Client2 client;
@@ -378,8 +351,7 @@
         }
         if (client != null) {
             try {
-                mMediaRouterService.requestCreateSession(
-                        client, route, controlCategory, requestId);
+                mMediaRouterService.requestCreateSession(client, route, routeType, requestId);
             } catch (RemoteException ex) {
                 Log.e(TAG, "Unable to request to create session.", ex);
                 mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
@@ -461,36 +433,6 @@
         }
     }
 
-    private void handleControlCategoriesChangedLocked(List<String> newControlCategories) {
-        List<MediaRoute2Info> addedRoutes = new ArrayList<>();
-        List<MediaRoute2Info> removedRoutes = new ArrayList<>();
-
-        List<String> prevControlCategories = mControlCategories;
-        mControlCategories = newControlCategories;
-
-        for (MediaRoute2Info route : mRoutes.values()) {
-            boolean preSupported = route.supportsControlCategories(prevControlCategories);
-            boolean postSupported = route.supportsControlCategories(newControlCategories);
-            if (preSupported == postSupported) {
-                continue;
-            }
-            if (preSupported) {
-                removedRoutes.add(route);
-            } else {
-                addedRoutes.add(route);
-            }
-        }
-
-        if (removedRoutes.size() > 0) {
-            mHandler.sendMessage(obtainMessage(MediaRouter2::notifyRoutesRemoved,
-                    MediaRouter2.this, removedRoutes));
-        }
-        if (addedRoutes.size() > 0) {
-            mHandler.sendMessage(obtainMessage(MediaRouter2::notifyRoutesAdded,
-                    MediaRouter2.this, addedRoutes));
-        }
-    }
-
     void addRoutesOnHandler(List<MediaRoute2Info> routes) {
         // TODO: When onRoutesAdded is first called,
         //  1) clear mRoutes before adding the routes
@@ -499,8 +441,8 @@
         List<MediaRoute2Info> addedRoutes = new ArrayList<>();
         synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
-                mRoutes.put(route.getUniqueId(), route);
-                if (route.supportsControlCategories(mControlCategories)) {
+                mRoutes.put(route.getId(), route);
+                if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) {
                     addedRoutes.add(route);
                 }
             }
@@ -515,8 +457,8 @@
         List<MediaRoute2Info> removedRoutes = new ArrayList<>();
         synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
-                mRoutes.remove(route.getUniqueId());
-                if (route.supportsControlCategories(mControlCategories)) {
+                mRoutes.remove(route.getId());
+                if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) {
                     removedRoutes.add(route);
                 }
             }
@@ -531,8 +473,8 @@
         List<MediaRoute2Info> changedRoutes = new ArrayList<>();
         synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
-                mRoutes.put(route.getUniqueId(), route);
-                if (route.supportsControlCategories(mControlCategories)) {
+                mRoutes.put(route.getId(), route);
+                if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) {
                     changedRoutes.add(route);
                 }
             }
@@ -562,27 +504,27 @@
             mSessionCreationRequests.remove(matchingRequest);
 
             MediaRoute2Info requestedRoute = matchingRequest.mRoute;
-            String requestedControlCategory = matchingRequest.mControlCategory;
+            String requestedRouteType = matchingRequest.mRouteType;
 
             if (sessionInfo == null) {
                 // TODO: We may need to distinguish between failure and rejection.
                 //       One way can be introducing 'reason'.
-                notifySessionCreationFailed(requestedRoute, requestedControlCategory);
+                notifySessionCreationFailed(requestedRoute, requestedRouteType);
                 return;
-            } else if (!TextUtils.equals(requestedControlCategory,
-                    sessionInfo.getControlCategory())) {
-                Log.w(TAG, "The session has different control category from what we requested. "
-                        + "(requested=" + requestedControlCategory
-                        + ", actual=" + sessionInfo.getControlCategory()
+            } else if (!TextUtils.equals(requestedRouteType,
+                    sessionInfo.getRouteType())) {
+                Log.w(TAG, "The session has different route type from what we requested. "
+                        + "(requested=" + requestedRouteType
+                        + ", actual=" + sessionInfo.getRouteType()
                         + ")");
-                notifySessionCreationFailed(requestedRoute, requestedControlCategory);
+                notifySessionCreationFailed(requestedRoute, requestedRouteType);
                 return;
             } else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) {
                 Log.w(TAG, "The session does not contain the requested route. "
                         + "(requestedRouteId=" + requestedRoute.getId()
                         + ", actualRoutes=" + sessionInfo.getSelectedRoutes()
                         + ")");
-                notifySessionCreationFailed(requestedRoute, requestedControlCategory);
+                notifySessionCreationFailed(requestedRoute, requestedRouteType);
                 return;
             } else if (!TextUtils.equals(requestedRoute.getProviderId(),
                     sessionInfo.getProviderId())) {
@@ -590,7 +532,7 @@
                         + "(requested route's providerId=" + requestedRoute.getProviderId()
                         + ", actual providerId=" + sessionInfo.getProviderId()
                         + ")");
-                notifySessionCreationFailed(requestedRoute, requestedControlCategory);
+                notifySessionCreationFailed(requestedRoute, requestedRouteType);
                 return;
             }
         }
@@ -598,7 +540,7 @@
         if (sessionInfo != null) {
             RouteSessionController controller = new RouteSessionController(sessionInfo);
             synchronized (sRouterLock) {
-                mSessionControllers.put(controller.getUniqueSessionId(), controller);
+                mSessionControllers.put(controller.getSessionId(), controller);
             }
             notifySessionCreated(controller);
         }
@@ -612,12 +554,12 @@
 
         RouteSessionController matchingController;
         synchronized (sRouterLock) {
-            matchingController = mSessionControllers.get(sessionInfo.getUniqueSessionId());
+            matchingController = mSessionControllers.get(sessionInfo.getId());
         }
 
         if (matchingController == null) {
             Log.w(TAG, "changeSessionInfoOnHandler: Matching controller not found. uniqueSessionId="
-                    + sessionInfo.getUniqueSessionId());
+                    + sessionInfo.getId());
             return;
         }
 
@@ -638,7 +580,7 @@
             return;
         }
 
-        final String uniqueSessionId = sessionInfo.getUniqueSessionId();
+        final String uniqueSessionId = sessionInfo.getId();
         RouteSessionController matchingController;
         synchronized (sRouterLock) {
             matchingController = mSessionControllers.get(uniqueSessionId);
@@ -647,7 +589,7 @@
         if (matchingController == null) {
             if (DEBUG) {
                 Log.d(TAG, "releaseControllerOnHandler: Matching controller not found. "
-                        + "uniqueSessionId=" + sessionInfo.getUniqueSessionId());
+                        + "uniqueSessionId=" + sessionInfo.getId());
             }
             return;
         }
@@ -666,24 +608,41 @@
         notifyControllerReleased(matchingController);
     }
 
+    private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
+            RouteDiscoveryRequest discoveryRequest) {
+        return routes.stream()
+                .filter(
+                        route -> route.containsRouteTypes(discoveryRequest.getRouteTypes()))
+                .collect(Collectors.toList());
+    }
+
     private void notifyRoutesAdded(List<MediaRoute2Info> routes) {
         for (RouteCallbackRecord record: mRouteCallbackRecords) {
-            record.mExecutor.execute(
-                    () -> record.mRouteCallback.onRoutesAdded(routes));
+            List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mRequest);
+            if (!filteredRoutes.isEmpty()) {
+                record.mExecutor.execute(
+                        () -> record.mRouteCallback.onRoutesAdded(filteredRoutes));
+            }
         }
     }
 
     private void notifyRoutesRemoved(List<MediaRoute2Info> routes) {
         for (RouteCallbackRecord record: mRouteCallbackRecords) {
-            record.mExecutor.execute(
-                    () -> record.mRouteCallback.onRoutesRemoved(routes));
+            List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mRequest);
+            if (!filteredRoutes.isEmpty()) {
+                record.mExecutor.execute(
+                        () -> record.mRouteCallback.onRoutesRemoved(filteredRoutes));
+            }
         }
     }
 
     private void notifyRoutesChanged(List<MediaRoute2Info> routes) {
         for (RouteCallbackRecord record: mRouteCallbackRecords) {
-            record.mExecutor.execute(
-                    () -> record.mRouteCallback.onRoutesChanged(routes));
+            List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mRequest);
+            if (!filteredRoutes.isEmpty()) {
+                record.mExecutor.execute(
+                        () -> record.mRouteCallback.onRoutesChanged(filteredRoutes));
+            }
         }
     }
 
@@ -694,10 +653,10 @@
         }
     }
 
-    private void notifySessionCreationFailed(MediaRoute2Info route, String controlCategory) {
+    private void notifySessionCreationFailed(MediaRoute2Info route, String routeType) {
         for (SessionCallbackRecord record: mSessionCallbackRecords) {
             record.mExecutor.execute(
-                    () -> record.mSessionCallback.onSessionCreationFailed(route, controlCategory));
+                    () -> record.mSessionCallback.onSessionCreationFailed(route, routeType));
         }
     }
 
@@ -764,10 +723,10 @@
          * Called when the session creation request failed.
          *
          * @param requestedRoute the route info which was used for the request
-         * @param requestedControlCategory the control category which was used for the request
+         * @param requestedRouteType the route type which was used for the request
          */
         public void onSessionCreationFailed(@NonNull MediaRoute2Info requestedRoute,
-                @NonNull String requestedControlCategory) {}
+                @NonNull String requestedRouteType) {}
 
         /**
          * Called when the session info has changed.
@@ -822,30 +781,19 @@
         /**
          * @return the ID of the session
          */
-        public int getSessionId() {
+        public String getSessionId() {
             synchronized (mControllerLock) {
-                return mSessionInfo.getSessionId();
+                return mSessionInfo.getId();
             }
         }
 
         /**
-         * @return the unique ID of the session
-         * @hide
+         * @return the type of routes that the session includes.
          */
         @NonNull
-        public String getUniqueSessionId() {
+        public String getRouteType() {
             synchronized (mControllerLock) {
-                return mSessionInfo.getUniqueSessionId();
-            }
-        }
-
-        /**
-         * @return the category of routes that the session includes.
-         */
-        @NonNull
-        public String getControlCategory() {
-            synchronized (mControllerLock) {
-                return mSessionInfo.getControlCategory();
+                return mSessionInfo.getRouteType();
             }
         }
 
@@ -935,13 +883,13 @@
             }
 
             List<MediaRoute2Info> selectedRoutes = getSelectedRoutes();
-            if (checkRouteListContainsRouteId(selectedRoutes, route.getUniqueId())) {
+            if (checkRouteListContainsRouteId(selectedRoutes, route.getId())) {
                 Log.w(TAG, "Ignoring selecting a route that is already selected. route=" + route);
                 return;
             }
 
             List<MediaRoute2Info> selectableRoutes = getSelectableRoutes();
-            if (!checkRouteListContainsRouteId(selectableRoutes, route.getUniqueId())) {
+            if (!checkRouteListContainsRouteId(selectableRoutes, route.getId())) {
                 Log.w(TAG, "Ignoring selecting a non-selectable route=" + route);
                 return;
             }
@@ -952,7 +900,7 @@
             }
             if (client != null) {
                 try {
-                    mMediaRouterService.selectRoute(client, getUniqueSessionId(), route);
+                    mMediaRouterService.selectRoute(client, getSessionId(), route);
                 } catch (RemoteException ex) {
                     Log.e(TAG, "Unable to select route for session.", ex);
                 }
@@ -982,13 +930,13 @@
             }
 
             List<MediaRoute2Info> selectedRoutes = getSelectedRoutes();
-            if (!checkRouteListContainsRouteId(selectedRoutes, route.getUniqueId())) {
+            if (!checkRouteListContainsRouteId(selectedRoutes, route.getId())) {
                 Log.w(TAG, "Ignoring deselecting a route that is not selected. route=" + route);
                 return;
             }
 
             List<MediaRoute2Info> deselectableRoutes = getDeselectableRoutes();
-            if (!checkRouteListContainsRouteId(deselectableRoutes, route.getUniqueId())) {
+            if (!checkRouteListContainsRouteId(deselectableRoutes, route.getId())) {
                 Log.w(TAG, "Ignoring deselecting a non-deselectable route=" + route);
                 return;
             }
@@ -999,7 +947,7 @@
             }
             if (client != null) {
                 try {
-                    mMediaRouterService.deselectRoute(client, getUniqueSessionId(), route);
+                    mMediaRouterService.deselectRoute(client, getSessionId(), route);
                 } catch (RemoteException ex) {
                     Log.e(TAG, "Unable to remove route from session.", ex);
                 }
@@ -1029,14 +977,14 @@
             }
 
             List<MediaRoute2Info> selectedRoutes = getSelectedRoutes();
-            if (checkRouteListContainsRouteId(selectedRoutes, route.getUniqueId())) {
+            if (checkRouteListContainsRouteId(selectedRoutes, route.getId())) {
                 Log.w(TAG, "Ignoring transferring to a route that is already added. route="
                         + route);
                 return;
             }
 
             List<MediaRoute2Info> transferrableRoutes = getTransferrableRoutes();
-            if (!checkRouteListContainsRouteId(transferrableRoutes, route.getUniqueId())) {
+            if (!checkRouteListContainsRouteId(transferrableRoutes, route.getId())) {
                 Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route);
                 return;
             }
@@ -1047,7 +995,7 @@
             }
             if (client != null) {
                 try {
-                    mMediaRouterService.transferToRoute(client, getUniqueSessionId(), route);
+                    mMediaRouterService.transferToRoute(client, getSessionId(), route);
                 } catch (RemoteException ex) {
                     Log.e(TAG, "Unable to transfer to route for session.", ex);
                 }
@@ -1072,20 +1020,24 @@
 
             Client2 client;
             synchronized (sRouterLock) {
-                mSessionControllers.remove(getUniqueSessionId(), this);
+                mSessionControllers.remove(getSessionId(), this);
                 client = mClient;
             }
             if (client != null) {
                 try {
-                    mMediaRouterService.releaseSession(client, getUniqueSessionId());
+                    mMediaRouterService.releaseSession(client, getSessionId());
                 } catch (RemoteException ex) {
                     Log.e(TAG, "Unable to notify of controller release", ex);
                 }
             }
         }
 
+        /**
+         * TODO: Change this to package private. (Hidden for debugging purposes)
+         * @hide
+         */
         @NonNull
-        RouteSessionInfo getRouteSessionInfo() {
+        public RouteSessionInfo getRouteSessionInfo() {
             synchronized (mControllerLock) {
                 return mSessionInfo;
             }
@@ -1100,11 +1052,12 @@
         // TODO: This method uses two locks (mLock outside, sLock inside).
         //       Check if there is any possiblity of deadlock.
         private List<MediaRoute2Info> getRoutesWithIdsLocked(List<String> routeIds) {
+
             List<MediaRoute2Info> routes = new ArrayList<>();
             synchronized (sRouterLock) {
+                // TODO: Maybe able to change using Collection.stream()?
                 for (String routeId : routeIds) {
-                    MediaRoute2Info route = mRoutes.get(
-                            MediaRoute2Info.toUniqueId(mSessionInfo.mProviderId, routeId));
+                    MediaRoute2Info route = mRoutes.get(routeId);
                     if (route != null) {
                         routes.add(route);
                     }
@@ -1117,13 +1070,13 @@
     final class RouteCallbackRecord {
         public final Executor mExecutor;
         public final RouteCallback mRouteCallback;
-        public final int mFlags;
+        public final RouteDiscoveryRequest mRequest;
 
         RouteCallbackRecord(@Nullable Executor executor, @NonNull RouteCallback routeCallback,
-                int flags) {
+                @Nullable RouteDiscoveryRequest request) {
             mRouteCallback = routeCallback;
             mExecutor = executor;
-            mFlags = flags;
+            mRequest = request;
         }
 
         @Override
@@ -1172,13 +1125,13 @@
 
     final class SessionCreationRequest {
         public final MediaRoute2Info mRoute;
-        public final String mControlCategory;
+        public final String mRouteType;
         public final int mRequestId;
 
         SessionCreationRequest(int requestId, @NonNull MediaRoute2Info route,
-                @NonNull String controlCategory) {
+                @NonNull String routeType) {
             mRoute = route;
-            mControlCategory = controlCategory;
+            mRouteType = routeType;
             mRequestId = requestId;
         }
     }
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 2e68e42..1e6ec51 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -65,7 +65,7 @@
     @GuardedBy("mRoutesLock")
     private final Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
     @NonNull
-    final ConcurrentMap<String, List<String>> mControlCategoryMap = new ConcurrentHashMap<>();
+    final ConcurrentMap<String, List<String>> mRouteTypeMap = new ConcurrentHashMap<>();
 
     private AtomicInteger mNextRequestId = new AtomicInteger(1);
 
@@ -144,7 +144,7 @@
                 }
                 //TODO: clear mRoutes?
                 mClient = null;
-                mControlCategoryMap.clear();
+                mRouteTypeMap.clear();
             }
         }
     }
@@ -160,14 +160,14 @@
     public List<MediaRoute2Info> getAvailableRoutes(@NonNull String packageName) {
         Objects.requireNonNull(packageName, "packageName must not be null");
 
-        List<String> controlCategories = mControlCategoryMap.get(packageName);
-        if (controlCategories == null) {
+        List<String> routeTypes = mRouteTypeMap.get(packageName);
+        if (routeTypes == null) {
             return Collections.emptyList();
         }
         List<MediaRoute2Info> routes = new ArrayList<>();
         synchronized (mRoutesLock) {
             for (MediaRoute2Info route : mRoutes.values()) {
-                if (route.supportsControlCategories(controlCategories)) {
+                if (route.containsRouteTypes(routeTypes)) {
                     routes.add(route);
                 }
             }
@@ -295,7 +295,7 @@
     void addRoutesOnHandler(List<MediaRoute2Info> routes) {
         synchronized (mRoutesLock) {
             for (MediaRoute2Info route : routes) {
-                mRoutes.put(route.getUniqueId(), route);
+                mRoutes.put(route.getId(), route);
             }
         }
         if (routes.size() > 0) {
@@ -306,7 +306,7 @@
     void removeRoutesOnHandler(List<MediaRoute2Info> routes) {
         synchronized (mRoutesLock) {
             for (MediaRoute2Info route : routes) {
-                mRoutes.remove(route.getUniqueId());
+                mRoutes.remove(route.getId());
             }
         }
         if (routes.size() > 0) {
@@ -317,7 +317,7 @@
     void changeRoutesOnHandler(List<MediaRoute2Info> routes) {
         synchronized (mRoutesLock) {
             for (MediaRoute2Info route : routes) {
-                mRoutes.put(route.getUniqueId(), route);
+                mRoutes.put(route.getId(), route);
             }
         }
         if (routes.size() > 0) {
@@ -352,15 +352,15 @@
         }
     }
 
-    void updateControlCategories(String packageName, List<String> categories) {
-        List<String> prevCategories = mControlCategoryMap.put(packageName, categories);
-        if ((prevCategories == null && categories.size() == 0)
-                || Objects.equals(categories, prevCategories)) {
+    void updateRouteTypes(String packageName, List<String> routeTypes) {
+        List<String> prevTypes = mRouteTypeMap.put(packageName, routeTypes);
+        if ((prevTypes == null && routeTypes.size() == 0)
+                || Objects.equals(routeTypes, prevTypes)) {
             return;
         }
         for (CallbackRecord record : mCallbackRecords) {
             record.mExecutor.execute(
-                    () -> record.mCallback.onControlCategoriesChanged(packageName, categories));
+                    () -> record.mCallback.onControlCategoriesChanged(packageName, routeTypes));
         }
     }
 
@@ -398,13 +398,13 @@
 
 
         /**
-         * Called when the control categories of an app is changed.
+         * Called when the route types of an app is changed.
          *
          * @param packageName the package name of the application
-         * @param controlCategories the list of control categories set by an application.
+         * @param routeTypes the list of route types set by an application.
          */
         public void onControlCategoriesChanged(@NonNull String packageName,
-                @NonNull List<String> controlCategories) {}
+                @NonNull List<String> routeTypes) {}
     }
 
     final class CallbackRecord {
@@ -440,10 +440,9 @@
                     MediaRouter2Manager.this, packageName, route));
         }
 
-        @Override
-        public void notifyControlCategoriesChanged(String packageName, List<String> categories) {
-            mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updateControlCategories,
-                    MediaRouter2Manager.this, packageName, categories));
+        public void notifyRouteTypesChanged(String packageName, List<String> routeTypes) {
+            mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updateRouteTypes,
+                    MediaRouter2Manager.this, packageName, routeTypes));
         }
 
         @Override
diff --git a/media/java/android/media/MediaRouter2Utils.java b/media/java/android/media/MediaRouter2Utils.java
new file mode 100644
index 0000000..4904582
--- /dev/null
+++ b/media/java/android/media/MediaRouter2Utils.java
@@ -0,0 +1,100 @@
+/*
+ * 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 android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class MediaRouter2Utils {
+
+    static final String TAG = "MR2Utils";
+    static final String SEPARATOR = ":";
+
+    /**
+     * @hide
+     */
+    @NonNull
+    public static String toUniqueId(@NonNull String providerId, @NonNull String id) {
+        if (TextUtils.isEmpty(providerId)) {
+            Log.w(TAG, "toUniqueId: providerId shouldn't be empty");
+            return null;
+        }
+        if (TextUtils.isEmpty(id)) {
+            Log.w(TAG, "toUniqueId: id shouldn't be null");
+            return null;
+        }
+
+        return providerId + SEPARATOR + id;
+    }
+
+    /**
+     * Gets provider ID from unique ID.
+     * If the corresponding provider ID could not be generated, it will return null.
+     *
+     * @hide
+     */
+    @Nullable
+    public static String getProviderId(@NonNull String uniqueId) {
+        if (TextUtils.isEmpty(uniqueId)) {
+            Log.w(TAG, "getProviderId: uniqueId shouldn't be empty");
+            return null;
+        }
+
+        int firstIndexOfSeparator = uniqueId.indexOf(SEPARATOR);
+        if (firstIndexOfSeparator == -1) {
+            return null;
+        }
+
+        String providerId = uniqueId.substring(0, firstIndexOfSeparator);
+        if (TextUtils.isEmpty(providerId)) {
+            return null;
+        }
+
+        return providerId;
+    }
+
+    /**
+     * Gets the original ID (i.e. non-unique route/session ID) from unique ID.
+     * If the corresponding ID could not be generated, it will return null.
+     *
+     * @hide
+     */
+    @Nullable
+    public static String getOriginalId(@NonNull String uniqueId) {
+        if (TextUtils.isEmpty(uniqueId)) {
+            Log.w(TAG, "getOriginalId: uniqueId shouldn't be empty");
+            return null;
+        }
+
+        int firstIndexOfSeparator = uniqueId.indexOf(SEPARATOR);
+        if (firstIndexOfSeparator == -1 || firstIndexOfSeparator + 1 >= uniqueId.length()) {
+            return null;
+        }
+
+        String providerId = uniqueId.substring(firstIndexOfSeparator + 1);
+        if (TextUtils.isEmpty(providerId)) {
+            return null;
+        }
+
+        return providerId;
+    }
+}
diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java
index 40e9073..05fa511 100644
--- a/media/java/android/media/MediaScannerConnection.java
+++ b/media/java/android/media/MediaScannerConnection.java
@@ -16,7 +16,7 @@
 
 package android.media;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
diff --git a/media/java/android/media/RouteDiscoveryRequest.aidl b/media/java/android/media/RouteDiscoveryRequest.aidl
new file mode 100644
index 0000000..744f656
--- /dev/null
+++ b/media/java/android/media/RouteDiscoveryRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+package android.media;
+
+parcelable RouteDiscoveryRequest;
diff --git a/media/java/android/media/RouteDiscoveryRequest.java b/media/java/android/media/RouteDiscoveryRequest.java
new file mode 100644
index 0000000..88b31fb
--- /dev/null
+++ b/media/java/android/media/RouteDiscoveryRequest.java
@@ -0,0 +1,200 @@
+/*
+ * 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 android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+
+/**
+ * @hide
+ */
+public final class RouteDiscoveryRequest implements Parcelable {
+    @NonNull
+    public static final Creator<RouteDiscoveryRequest> CREATOR =
+            new Creator<RouteDiscoveryRequest>() {
+                @Override
+                public RouteDiscoveryRequest createFromParcel(Parcel in) {
+                    return new RouteDiscoveryRequest(in);
+                }
+
+                @Override
+                public RouteDiscoveryRequest[] newArray(int size) {
+                    return new RouteDiscoveryRequest[size];
+                }
+            };
+
+    @NonNull
+    private final List<String> mRouteTypes;
+    private final boolean mActiveScan;
+    @Nullable
+    private final Bundle mExtras;
+
+    /**
+     * @hide
+     */
+    public static final RouteDiscoveryRequest EMPTY =
+            new Builder(Collections.emptyList(), false).build();
+
+    RouteDiscoveryRequest(@NonNull Builder builder) {
+        mRouteTypes = builder.mRouteTypes;
+        mActiveScan = builder.mActiveScan;
+        mExtras = builder.mExtras;
+    }
+
+    RouteDiscoveryRequest(@NonNull Parcel in) {
+        mRouteTypes = in.createStringArrayList();
+        mActiveScan = in.readBoolean();
+        mExtras = in.readBundle();
+    }
+
+    @NonNull
+    public List<String> getRouteTypes() {
+        return mRouteTypes;
+    }
+
+    public boolean isActiveScan() {
+        return mActiveScan;
+    }
+
+    /**
+     * @hide
+     */
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeStringList(mRouteTypes);
+        dest.writeBoolean(mActiveScan);
+        dest.writeBundle(mExtras);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder()
+                .append("RouteDiscoveryRequest{ ")
+                .append("routeTypes={")
+                .append(String.join(", ", mRouteTypes))
+                .append("}")
+                .append(", activeScan=")
+                .append(mActiveScan)
+                .append(" }");
+
+        return result.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof RouteDiscoveryRequest)) {
+            return false;
+        }
+        RouteDiscoveryRequest other = (RouteDiscoveryRequest) o;
+        return Objects.equals(mRouteTypes, other.mRouteTypes)
+                && mActiveScan == other.mActiveScan;
+    }
+
+    /**
+     * Builder for {@link RouteDiscoveryRequest}.
+     */
+    public static final class Builder {
+        List<String> mRouteTypes;
+        boolean mActiveScan;
+        Bundle mExtras;
+
+        public Builder(@NonNull List<String> routeTypes, boolean activeScan) {
+            mRouteTypes = new ArrayList<>(
+                    Objects.requireNonNull(routeTypes, "routeTypes must not be null"));
+            mActiveScan = activeScan;
+        }
+
+        public Builder(@NonNull RouteDiscoveryRequest request) {
+            Objects.requireNonNull(request, "request must not be null");
+
+            mRouteTypes = request.getRouteTypes();
+            mActiveScan = request.isActiveScan();
+            mExtras = request.getExtras();
+        }
+
+        /**
+         * A constructor to combine all of the requests into a single request.
+         * It ignores extras of requests.
+         */
+        Builder(@NonNull Collection<RouteDiscoveryRequest> requests) {
+            Set<String> routeTypeSet = new HashSet<>();
+            mActiveScan = false;
+            for (RouteDiscoveryRequest request : requests) {
+                routeTypeSet.addAll(request.mRouteTypes);
+                mActiveScan |= request.mActiveScan;
+            }
+            mRouteTypes = new ArrayList<>(routeTypeSet);
+        }
+
+        /**
+         * Sets route types to discover.
+         */
+        public Builder setRouteTypes(@NonNull List<String> routeTypes) {
+            mRouteTypes = new ArrayList<>(
+                    Objects.requireNonNull(routeTypes, "routeTypes must not be null"));
+            return this;
+        }
+
+        /**
+         * Sets if active scanning should be performed.
+         */
+        public Builder setActiveScan(boolean activeScan) {
+            mActiveScan = activeScan;
+            return this;
+        }
+
+        /**
+         * Sets the extras of the route.
+         * @hide
+         */
+        public Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds the {@link RouteDiscoveryRequest}.
+         */
+        public RouteDiscoveryRequest build() {
+            return new RouteDiscoveryRequest(this);
+        }
+    }
+}
diff --git a/media/java/android/media/RouteSessionInfo.java b/media/java/android/media/RouteSessionInfo.java
index 2d7bc24..5330630 100644
--- a/media/java/android/media/RouteSessionInfo.java
+++ b/media/java/android/media/RouteSessionInfo.java
@@ -22,6 +22,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -33,6 +34,7 @@
  * @hide
  */
 public class RouteSessionInfo implements Parcelable {
+
     @NonNull
     public static final Creator<RouteSessionInfo> CREATOR =
             new Creator<RouteSessionInfo>() {
@@ -46,9 +48,11 @@
                 }
             };
 
-    final int mSessionId;
-    final String mPackageName;
-    final String mControlCategory;
+    public static final String TAG = "RouteSessionInfo";
+
+    final String mId;
+    final String mClientPackageName;
+    final String mRouteType;
     @Nullable
     final String mProviderId;
     final List<String> mSelectedRoutes;
@@ -61,15 +65,19 @@
     RouteSessionInfo(@NonNull Builder builder) {
         Objects.requireNonNull(builder, "builder must not be null.");
 
-        mSessionId = builder.mSessionId;
-        mPackageName = builder.mPackageName;
-        mControlCategory = builder.mControlCategory;
+        mId = builder.mId;
+        mClientPackageName = builder.mClientPackageName;
+        mRouteType = builder.mRouteType;
         mProviderId = builder.mProviderId;
 
-        mSelectedRoutes = Collections.unmodifiableList(builder.mSelectedRoutes);
-        mSelectableRoutes = Collections.unmodifiableList(builder.mSelectableRoutes);
-        mDeselectableRoutes = Collections.unmodifiableList(builder.mDeselectableRoutes);
-        mTransferrableRoutes = Collections.unmodifiableList(builder.mTransferrableRoutes);
+        mSelectedRoutes = Collections.unmodifiableList(
+                convertToUniqueRouteIds(builder.mSelectedRoutes));
+        mSelectableRoutes = Collections.unmodifiableList(
+                convertToUniqueRouteIds(builder.mSelectableRoutes));
+        mDeselectableRoutes = Collections.unmodifiableList(
+                convertToUniqueRouteIds(builder.mDeselectableRoutes));
+        mTransferrableRoutes = Collections.unmodifiableList(
+                convertToUniqueRouteIds(builder.mTransferrableRoutes));
 
         mControlHints = builder.mControlHints;
     }
@@ -77,9 +85,9 @@
     RouteSessionInfo(@NonNull Parcel src) {
         Objects.requireNonNull(src, "src must not be null.");
 
-        mSessionId = src.readInt();
-        mPackageName = ensureString(src.readString());
-        mControlCategory = ensureString(src.readString());
+        mId = ensureString(src.readString());
+        mClientPackageName = ensureString(src.readString());
+        mRouteType = ensureString(src.readString());
         mProviderId = src.readString();
 
         mSelectedRoutes = ensureList(src.createStringArrayList());
@@ -105,82 +113,59 @@
     }
 
     /**
-     * Gets non-unique session id (int) from unique session id (string).
-     * If the corresponding session id could not be generated, it will return null.
-     * @hide
-     */
-    @Nullable
-    public static Integer getSessionId(@NonNull String uniqueSessionId) {
-        int lastIndexOfSeparator = uniqueSessionId.lastIndexOf("/");
-        if (lastIndexOfSeparator == -1 || lastIndexOfSeparator + 1 >= uniqueSessionId.length()) {
-            return null;
-        }
-
-        String integerString = uniqueSessionId.substring(lastIndexOfSeparator + 1);
-        if (TextUtils.isEmpty(integerString)) {
-            return null;
-        }
-
-        try {
-            return Integer.parseInt(integerString);
-        } catch (NumberFormatException ex) {
-            return null;
-        }
-    }
-
-    /**
-     * Gets provider ID (string) from unique session id (string).
-     * If the corresponding provider ID could not be generated, it will return null.
-     * @hide
-     *
-     * TODO: This logic seems error-prone. Consider to use long uniqueId.
-     */
-    @Nullable
-    public static String getProviderId(@NonNull String uniqueSessionId) {
-        int lastIndexOfSeparator = uniqueSessionId.lastIndexOf("/");
-        if (lastIndexOfSeparator == -1) {
-            return null;
-        }
-
-        String result = uniqueSessionId.substring(0, lastIndexOfSeparator);
-        if (TextUtils.isEmpty(result)) {
-            return null;
-        }
-        return result;
-    }
-
-    /**
      * Returns whether the session info is valid or not
+     *
+     * TODO in this CL: Remove this method.
      */
     public boolean isValid() {
-        return !TextUtils.isEmpty(mPackageName)
-                && !TextUtils.isEmpty(mControlCategory)
+        return !TextUtils.isEmpty(mId)
+                && !TextUtils.isEmpty(mClientPackageName)
+                && !TextUtils.isEmpty(mRouteType)
                 && mSelectedRoutes.size() > 0;
     }
 
     /**
-     * Gets the id of the session
+     * Gets the id of the session. The sessions which are given by {@link MediaRouter2} will have
+     * unique IDs.
+     * <p>
+     * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method
+     * can be different from what was set in {@link MediaRoute2ProviderService}.
+     *
+     * @see Builder#Builder(String, String, String)
      */
     @NonNull
-    public int getSessionId() {
-        return mSessionId;
+    public String getId() {
+        if (mProviderId != null) {
+            return MediaRouter2Utils.toUniqueId(mProviderId, mId);
+        } else {
+            return mId;
+        }
+    }
+
+    /**
+     * Gets the original id set by {@link Builder#Builder(String, String, String)}.
+     * @hide
+     */
+    @NonNull
+    public String getOriginalId() {
+        return mId;
     }
 
     /**
      * Gets the client package name of the session
      */
     @NonNull
-    public String getPackageName() {
-        return mPackageName;
+    public String getClientPackageName() {
+        return mClientPackageName;
     }
 
     /**
-     * Gets the control category of the session.
-     * Routes that don't support the category can't be added to the session.
+     * Gets the route type of the session.
+     * Routes that don't have the type can't be added to the session.
      */
     @NonNull
-    public String getControlCategory() {
-        return mControlCategory;
+    public String getRouteType() {
+        return mRouteType;
     }
 
     /**
@@ -193,19 +178,6 @@
     }
 
     /**
-     * Gets the unique id of the session.
-     * @hide
-     */
-    @NonNull
-    public String getUniqueSessionId() {
-        StringBuilder sessionIdBuilder = new StringBuilder()
-                .append(mProviderId)
-                .append("/")
-                .append(mSessionId);
-        return sessionIdBuilder.toString();
-    }
-
-    /**
      * Gets the list of ids of selected routes for the session. It shouldn't be empty.
      */
     @NonNull
@@ -252,9 +224,9 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mSessionId);
-        dest.writeString(mPackageName);
-        dest.writeString(mControlCategory);
+        dest.writeString(mId);
+        dest.writeString(mClientPackageName);
+        dest.writeString(mRouteType);
         dest.writeString(mProviderId);
         dest.writeStringList(mSelectedRoutes);
         dest.writeStringList(mSelectableRoutes);
@@ -267,8 +239,8 @@
     public String toString() {
         StringBuilder result = new StringBuilder()
                 .append("RouteSessionInfo{ ")
-                .append("sessionId=").append(mSessionId)
-                .append(", controlCategory=").append(mControlCategory)
+                .append("sessionId=").append(mId)
+                .append(", routeType=").append(mRouteType)
                 .append(", selectedRoutes={")
                 .append(String.join(",", mSelectedRoutes))
                 .append("}")
@@ -285,13 +257,31 @@
         return result.toString();
     }
 
+    private List<String> convertToUniqueRouteIds(@NonNull List<String> routeIds) {
+        if (routeIds == null) {
+            Log.w(TAG, "routeIds is null. Returning an empty list");
+            return Collections.emptyList();
+        }
+
+        // mProviderId can be null if not set. Return the original list for this case.
+        if (mProviderId == null) {
+            return routeIds;
+        }
+
+        List<String> result = new ArrayList<>();
+        for (String routeId : routeIds) {
+            result.add(MediaRouter2Utils.toUniqueId(mProviderId, routeId));
+        }
+        return result;
+    }
+
     /**
      * Builder class for {@link RouteSessionInfo}.
      */
     public static final class Builder {
-        final String mPackageName;
-        final int mSessionId;
-        final String mControlCategory;
+        final String mId;
+        final String mClientPackageName;
+        final String mRouteType;
         String mProviderId;
         final List<String> mSelectedRoutes;
         final List<String> mSelectableRoutes;
@@ -299,23 +289,43 @@
         final List<String> mTransferrableRoutes;
         Bundle mControlHints;
 
-        public Builder(int sessionId, @NonNull String packageName,
-                @NonNull String controlCategory) {
-            mSessionId = sessionId;
-            mPackageName = Objects.requireNonNull(packageName, "packageName must not be null");
-            mControlCategory = Objects.requireNonNull(controlCategory,
-                    "controlCategory must not be null");
-
+        /**
+         * Constructor for builder to create {@link RouteSessionInfo}.
+         * <p>
+         * In order to ensure ID uniqueness in {@link MediaRouter2} side, the value of
+         * {@link RouteSessionInfo#getId()} can be different from what was set in
+         * {@link MediaRoute2ProviderService}.
+         * </p>
+         *
+         * @see MediaRoute2Info#getId()
+         */
+        public Builder(@NonNull String id, @NonNull String clientPackageName,
+                @NonNull String routeType) {
+            if (TextUtils.isEmpty(id)) {
+                throw new IllegalArgumentException("id must not be empty");
+            }
+            mId = id;
+            mClientPackageName = Objects.requireNonNull(
+                    clientPackageName, "clientPackageName must not be null");
+            mRouteType = Objects.requireNonNull(routeType, "routeType must not be null");
             mSelectedRoutes = new ArrayList<>();
             mSelectableRoutes = new ArrayList<>();
             mDeselectableRoutes = new ArrayList<>();
             mTransferrableRoutes = new ArrayList<>();
         }
 
-        public Builder(RouteSessionInfo sessionInfo) {
-            mSessionId = sessionInfo.mSessionId;
-            mPackageName = sessionInfo.mPackageName;
-            mControlCategory = sessionInfo.mControlCategory;
+        /**
+         * Constructor for builder to create {@link RouteSessionInfo} with
+         * existing {@link RouteSessionInfo} instance.
+         *
+         * @param sessionInfo the existing instance to copy data from.
+         */
+        public Builder(@NonNull RouteSessionInfo sessionInfo) {
+            Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+
+            mId = sessionInfo.mId;
+            mClientPackageName = sessionInfo.mClientPackageName;
+            mRouteType = sessionInfo.mRouteType;
             mProviderId = sessionInfo.mProviderId;
 
             mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes);
@@ -327,10 +337,17 @@
         }
 
         /**
-         * Sets the provider id of the session.
+         * Sets the provider ID of the session.
+         * Also, calling this method will make all type of route IDs be unique by adding
+         * {@code providerId:} as a prefix. So do NOT call this method twice on same instance.
+         *
+         * @hide
          */
         @NonNull
-        public Builder setProviderId(String providerId) {
+        public Builder setProviderId(@NonNull String providerId) {
+            if (TextUtils.isEmpty(providerId)) {
+                throw new IllegalArgumentException("providerId must not be empty");
+            }
             mProviderId = providerId;
             return this;
         }
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
index 56e5566..77596a5 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
@@ -22,7 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
 import android.hardware.soundtrigger.SoundTrigger;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 61b3e76..1c38301 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -23,7 +23,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.hardware.soundtrigger.ModelParams;
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 1b9cac0..377b2bc 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -20,7 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.StringRes;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 5c11ed9b..7fbb337 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -22,9 +22,9 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.Service;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.PixelFormat;
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index ab7bbca..4318a0a 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -62,6 +62,8 @@
     private final int mAudioChannelCount;
     private final int mAudioSampleRate;
     private final boolean mAudioDescription;
+    private final boolean mHardOfHearing;
+    private final boolean mSpokenSubtitle;
     private final int mVideoWidth;
     private final int mVideoHeight;
     private final float mVideoFrameRate;
@@ -72,8 +74,9 @@
 
     private TvTrackInfo(int type, String id, String language, CharSequence description,
             boolean encrypted, int audioChannelCount, int audioSampleRate, boolean audioDescription,
-            int videoWidth, int videoHeight, float videoFrameRate, float videoPixelAspectRatio,
-            byte videoActiveFormatDescription, Bundle extra) {
+            boolean hardOfHearing, boolean spokenSubtitle, int videoWidth, int videoHeight,
+            float videoFrameRate, float videoPixelAspectRatio, byte videoActiveFormatDescription,
+            Bundle extra) {
         mType = type;
         mId = id;
         mLanguage = language;
@@ -82,6 +85,8 @@
         mAudioChannelCount = audioChannelCount;
         mAudioSampleRate = audioSampleRate;
         mAudioDescription = audioDescription;
+        mHardOfHearing = hardOfHearing;
+        mSpokenSubtitle = spokenSubtitle;
         mVideoWidth = videoWidth;
         mVideoHeight = videoHeight;
         mVideoFrameRate = videoFrameRate;
@@ -99,6 +104,8 @@
         mAudioChannelCount = in.readInt();
         mAudioSampleRate = in.readInt();
         mAudioDescription = in.readInt() != 0;
+        mHardOfHearing = in.readInt() != 0;
+        mSpokenSubtitle = in.readInt() != 0;
         mVideoWidth = in.readInt();
         mVideoHeight = in.readInt();
         mVideoFrameRate = in.readFloat();
@@ -192,6 +199,39 @@
     }
 
     /**
+     * Returns {@code true} if the track is intended for people with hearing impairment, {@code
+     * false} otherwise. Valid only for {@link #TYPE_AUDIO} and {@link #TYPE_SUBTITLE} tracks.
+     *
+     * <p>For example of broadcast, hard of hearing information may be referred to broadcast
+     * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio Language
+     * Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN 300 468).
+     *
+     * @throws IllegalStateException if not called on an audio track or a subtitle track
+     */
+    public boolean isHardOfHearing() {
+        if (mType != TYPE_AUDIO && mType != TYPE_SUBTITLE) {
+            throw new IllegalStateException("Not an audio or a subtitle track");
+        }
+        return mHardOfHearing;
+    }
+
+    /**
+     * Returns {@code true} if the track is a spoken subtitle for people with visual impairment,
+     * {@code false} otherwise. Valid only for {@link #TYPE_AUDIO} tracks.
+     *
+     * <p>For example of broadcast, spoken subtitle information may be referred to broadcast
+     * standard (e.g. Supplementary Audio Language Descriptor of ETSI EN 300 468).
+     *
+     * @throws IllegalStateException if not called on an audio track
+     */
+    public boolean isSpokenSubtitle() {
+        if (mType != TYPE_AUDIO) {
+            throw new IllegalStateException("Not an audio track");
+        }
+        return mSpokenSubtitle;
+    }
+
+    /**
      * Returns the width of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO}
      * tracks.
      *
@@ -287,6 +327,8 @@
         dest.writeInt(mAudioChannelCount);
         dest.writeInt(mAudioSampleRate);
         dest.writeInt(mAudioDescription ? 1 : 0);
+        dest.writeInt(mHardOfHearing ? 1 : 0);
+        dest.writeInt(mSpokenSubtitle ? 1 : 0);
         dest.writeInt(mVideoWidth);
         dest.writeInt(mVideoHeight);
         dest.writeFloat(mVideoFrameRate);
@@ -310,6 +352,7 @@
         if (!TextUtils.equals(mId, obj.mId) || mType != obj.mType
                 || !TextUtils.equals(mLanguage, obj.mLanguage)
                 || !TextUtils.equals(mDescription, obj.mDescription)
+                || mEncrypted != obj.mEncrypted
                 || !Objects.equals(mExtra, obj.mExtra)) {
             return false;
         }
@@ -318,7 +361,9 @@
             case TYPE_AUDIO:
                 return mAudioChannelCount == obj.mAudioChannelCount
                         && mAudioSampleRate == obj.mAudioSampleRate
-                        && mAudioDescription == obj.mAudioDescription;
+                        && mAudioDescription == obj.mAudioDescription
+                        && mHardOfHearing == obj.mHardOfHearing
+                        && mSpokenSubtitle == obj.mSpokenSubtitle;
 
             case TYPE_VIDEO:
                 return mVideoWidth == obj.mVideoWidth
@@ -326,6 +371,9 @@
                         && mVideoFrameRate == obj.mVideoFrameRate
                         && mVideoPixelAspectRatio == obj.mVideoPixelAspectRatio
                         && mVideoActiveFormatDescription == obj.mVideoActiveFormatDescription;
+
+            case TYPE_SUBTITLE:
+                return mHardOfHearing == obj.mHardOfHearing;
         }
 
         return true;
@@ -361,6 +409,8 @@
         private int mAudioChannelCount;
         private int mAudioSampleRate;
         private boolean mAudioDescription;
+        private boolean mHardOfHearing;
+        private boolean mSpokenSubtitle;
         private int mVideoWidth;
         private int mVideoHeight;
         private float mVideoFrameRate;
@@ -474,6 +524,46 @@
         }
 
         /**
+         * Sets the hard of hearing attribute of the track. Valid only for {@link #TYPE_AUDIO} and
+         * {@link #TYPE_SUBTITLE} tracks.
+         *
+         * <p>For example of broadcast, hard of hearing information may be referred to broadcast
+         * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio
+         * Language Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN
+         * 300 468).
+         *
+         * @param hardOfHearing The hard of hearing attribute of the track.
+         * @throws IllegalStateException if not called on an audio track or a subtitle track
+         */
+        @NonNull
+        public Builder setHardOfHearing(boolean hardOfHearing) {
+            if (mType != TYPE_AUDIO && mType != TYPE_SUBTITLE) {
+                throw new IllegalStateException("Not an audio track or a subtitle track");
+            }
+            mHardOfHearing = hardOfHearing;
+            return this;
+        }
+
+        /**
+         * Sets the spoken subtitle attribute of the audio. Valid only for {@link #TYPE_AUDIO}
+         * tracks.
+         *
+         * <p>For example of broadcast, spoken subtitle information may be referred to broadcast
+         * standard (e.g. Supplementary Audio Language Descriptor of ETSI EN 300 468).
+         *
+         * @param spokenSubtitle The spoken subtitle attribute of the audio.
+         * @throws IllegalStateException if not called on an audio track
+         */
+        @NonNull
+        public Builder setSpokenSubtitle(boolean spokenSubtitle) {
+            if (mType != TYPE_AUDIO) {
+                throw new IllegalStateException("Not an audio track");
+            }
+            mSpokenSubtitle = spokenSubtitle;
+            return this;
+        }
+
+        /**
          * Sets the width of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO}
          * tracks.
          *
@@ -575,9 +665,9 @@
          */
         public TvTrackInfo build() {
             return new TvTrackInfo(mType, mId, mLanguage, mDescription, mEncrypted,
-                    mAudioChannelCount, mAudioSampleRate, mAudioDescription, mVideoWidth,
-                    mVideoHeight, mVideoFrameRate, mVideoPixelAspectRatio,
-                    mVideoActiveFormatDescription, mExtra);
+                    mAudioChannelCount, mAudioSampleRate, mAudioDescription, mHardOfHearing,
+                    mSpokenSubtitle, mVideoWidth, mVideoHeight, mVideoFrameRate,
+                    mVideoPixelAspectRatio, mVideoActiveFormatDescription, mExtra);
         }
     }
 }
diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java
new file mode 100644
index 0000000..f9f7a22
--- /dev/null
+++ b/media/java/android/media/tv/tuner/Descrambler.java
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner;
+
+import android.annotation.Nullable;
+import android.media.tv.tuner.Tuner.Filter;
+import android.media.tv.tuner.TunerConstants.DemuxPidType;
+
+/**
+ * This class is used to interact with descramblers.
+ *
+ * <p> Descrambler is a hardware component used to descramble data.
+ *
+ * <p> This class controls the TIS interaction with Tuner HAL.
+ * @hide
+ */
+public class Descrambler implements AutoCloseable {
+    private long mNativeContext;
+
+    private native int nativeAddPid(int pidType, int pid, Filter filter);
+    private native int nativeRemovePid(int pidType, int pid, Filter filter);
+    private native int nativeSetKeyToken(byte[] keyToken);
+    private native int nativeClose();
+
+    private Descrambler() {}
+
+    /**
+     * Add packets' PID to the descrambler for descrambling.
+     *
+     * The descrambler will start descrambling packets with this PID. Multiple PIDs can be added
+     * into one descrambler instance because descambling can happen simultaneously on packets
+     * from different PIDs.
+     *
+     * If the Descrambler previously contained a filter for the PID, the old filter is replaced
+     * by the specified filter.
+     *
+     * @param pidType the type of the PID.
+     * @param pid the PID of packets to start to be descrambled.
+     * @param filter an optional filter instance to identify upper stream.
+     * @return result status of the operation.
+     *
+     * @hide
+     */
+    public int addPid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) {
+        return nativeAddPid(pidType, pid, filter);
+    }
+
+    /**
+     * Remove packets' PID from the descrambler
+     *
+     * The descrambler will stop descrambling packets with this PID.
+     *
+     * @param pidType the type of the PID.
+     * @param pid the PID of packets to stop to be descrambled.
+     * @param filter an optional filter instance to identify upper stream.
+     * @return result status of the operation.
+     *
+     * @hide
+     */
+    public int removePid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) {
+        return nativeRemovePid(pidType, pid, filter);
+    }
+
+    /**
+     * Set a key token to link descrambler to a key slot
+     *
+     * A descrambler instance can have only one key slot to link, but a key slot can hold a few
+     * keys for different purposes.
+     *
+     * @param keyToken the token to be used to link the key slot.
+     * @return result status of the operation.
+     *
+     * @hide
+     */
+    public int setKeyToken(byte[] keyToken) {
+        return nativeSetKeyToken(keyToken);
+    }
+
+    /**
+     * Release the descrambler instance.
+     *
+     * @hide
+     */
+    @Override
+    public void close() {
+        nativeClose();
+    }
+
+}
diff --git a/media/java/android/media/tv/tuner/Dvr.java b/media/java/android/media/tv/tuner/Dvr.java
new file mode 100644
index 0000000..0bfba8f
--- /dev/null
+++ b/media/java/android/media/tv/tuner/Dvr.java
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner;
+
+import android.annotation.NonNull;
+import android.media.tv.tuner.Tuner.DvrCallback;
+import android.media.tv.tuner.Tuner.Filter;
+import android.os.ParcelFileDescriptor;
+
+/** @hide */
+public class Dvr {
+    private long mNativeContext;
+    private DvrCallback mCallback;
+
+    private native int nativeAttachFilter(Filter filter);
+    private native int nativeDetachFilter(Filter filter);
+    private native int nativeConfigureDvr(DvrSettings settings);
+    private native int nativeStartDvr();
+    private native int nativeStopDvr();
+    private native int nativeFlushDvr();
+    private native int nativeClose();
+    private native void nativeSetFileDescriptor(int fd);
+    private native int nativeRead(int size);
+    private native int nativeRead(byte[] bytes, int offset, int size);
+    private native int nativeWrite(int size);
+    private native int nativeWrite(byte[] bytes, int offset, int size);
+
+    private Dvr() {}
+
+    /**
+     * Attaches a filter to DVR interface for recording.
+     *
+     * @param filter the filter to be attached.
+     * @return result status of the operation.
+     */
+    public int attachFilter(Filter filter) {
+        return nativeAttachFilter(filter);
+    }
+
+    /**
+     * Detaches a filter from DVR interface.
+     *
+     * @param filter the filter to be detached.
+     * @return result status of the operation.
+     */
+    public int detachFilter(Filter filter) {
+        return nativeDetachFilter(filter);
+    }
+
+    /**
+     * Configures the DVR.
+     *
+     * @param settings the settings of the DVR interface.
+     * @return result status of the operation.
+     */
+    public int configure(DvrSettings settings) {
+        return nativeConfigureDvr(settings);
+    }
+
+    /**
+     * Starts DVR.
+     *
+     * Starts consuming playback data or producing data for recording.
+     *
+     * @return result status of the operation.
+     */
+    public int start() {
+        return nativeStartDvr();
+    }
+
+    /**
+     * Stops DVR.
+     *
+     * Stops consuming playback data or producing data for recording.
+     *
+     * @return result status of the operation.
+     */
+    public int stop() {
+        return nativeStopDvr();
+    }
+
+    /**
+     * Flushed DVR data.
+     *
+     * @return result status of the operation.
+     */
+    public int flush() {
+        return nativeFlushDvr();
+    }
+
+    /**
+     * closes the DVR instance to release resources.
+     *
+     * @return result status of the operation.
+     */
+    public int close() {
+        return nativeClose();
+    }
+
+    /**
+     * Sets file descriptor to read/write data.
+     */
+    public void setFileDescriptor(ParcelFileDescriptor fd) {
+        nativeSetFileDescriptor(fd.getFd());
+    }
+
+    /**
+     * Reads data from the file for DVR playback.
+     */
+    public int read(int size) {
+        return nativeRead(size);
+    }
+
+    /**
+     * Reads data from the buffer for DVR playback.
+     */
+    public int read(@NonNull byte[] bytes, int offset, int size) {
+        if (size + offset > bytes.length) {
+            throw new ArrayIndexOutOfBoundsException(
+                    "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
+        }
+        return nativeRead(bytes, offset, size);
+    }
+
+    /**
+     * Writes recording data to file.
+     */
+    public int write(int size) {
+        return nativeWrite(size);
+    }
+
+    /**
+     * Writes recording data to buffer.
+     */
+    public int write(@NonNull byte[] bytes, int offset, int size) {
+        return nativeWrite(bytes, offset, size);
+    }
+}
diff --git a/media/java/android/media/tv/tuner/Filter.java b/media/java/android/media/tv/tuner/Filter.java
new file mode 100644
index 0000000..db3b97a
--- /dev/null
+++ b/media/java/android/media/tv/tuner/Filter.java
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.tv.tuner.Tuner.FilterCallback;
+import android.media.tv.tuner.filter.FilterConfiguration;
+import android.media.tv.tuner.filter.Settings;
+
+/**
+ * Tuner data filter.
+ *
+ * <p> This class is used to filter wanted data according to the filter's configuration.
+ * @hide
+ */
+public class Filter implements AutoCloseable {
+    private long mNativeContext;
+    private FilterCallback mCallback;
+    int mId;
+
+    private native int nativeConfigureFilter(
+            int type, int subType, FilterConfiguration settings);
+    private native int nativeGetId();
+    private native int nativeSetDataSource(Tuner.Filter source);
+    private native int nativeStartFilter();
+    private native int nativeStopFilter();
+    private native int nativeFlushFilter();
+    private native int nativeRead(byte[] buffer, int offset, int size);
+    private native int nativeClose();
+
+    private Filter(int id) {
+        mId = id;
+    }
+
+    private void onFilterStatus(int status) {
+    }
+
+    /**
+     * Configures the filter.
+     *
+     * @param settings the settings of the filter.
+     * @return result status of the operation.
+     * @hide
+     */
+    public int configure(FilterConfiguration settings) {
+        int subType = -1;
+        Settings s = settings.getSettings();
+        if (s != null) {
+            subType = s.getType();
+        }
+        return nativeConfigureFilter(settings.getType(), subType, settings);
+    }
+
+    /**
+     * Gets the filter Id.
+     *
+     * @return the hardware resource Id for the filter.
+     * @hide
+     */
+    public int getId() {
+        return nativeGetId();
+    }
+
+    /**
+     * Sets the filter's data source.
+     *
+     * A filter uses demux as data source by default. If the data was packetized
+     * by multiple protocols, multiple filters may need to work together to
+     * extract all protocols' header. Then a filter's data source can be output
+     * from another filter.
+     *
+     * @param source the filter instance which provides data input. Switch to
+     * use demux as data source if the filter instance is NULL.
+     * @return result status of the operation.
+     * @hide
+     */
+    public int setDataSource(@Nullable Tuner.Filter source) {
+        return nativeSetDataSource(source);
+    }
+
+    /**
+     * Starts the filter.
+     *
+     * @return result status of the operation.
+     * @hide
+     */
+    public int start() {
+        return nativeStartFilter();
+    }
+
+
+    /**
+     * Stops the filter.
+     *
+     * @return result status of the operation.
+     * @hide
+     */
+    public int stop() {
+        return nativeStopFilter();
+    }
+
+    /**
+     * Flushes the filter.
+     *
+     * @return result status of the operation.
+     * @hide
+     */
+    public int flush() {
+        return nativeFlushFilter();
+    }
+
+    /** @hide */
+    public int read(@NonNull byte[] buffer, int offset, int size) {
+        size = Math.min(size, buffer.length - offset);
+        return nativeRead(buffer, offset, size);
+    }
+
+    /**
+     * Release the Filter instance.
+     *
+     * @hide
+     */
+    @Override
+    public void close() {
+        nativeClose();
+    }
+}
diff --git a/media/java/android/media/tv/tuner/FilterConfiguration.java b/media/java/android/media/tv/tuner/FilterConfiguration.java
deleted file mode 100644
index b80a82a..0000000
--- a/media/java/android/media/tv/tuner/FilterConfiguration.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * 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.
- */
-
-package android.media.tv.tuner;
-
-import android.annotation.Nullable;
-import android.media.tv.tuner.TunerConstants.FilterType;
-
-import java.util.List;
-
-/**
- * Demux Filter configuration.
- *
- * @hide
- */
-public abstract class FilterConfiguration {
-    @Nullable
-    protected final Settings mSettings;
-
-    protected FilterConfiguration(Settings settings) {
-        mSettings = settings;
-    }
-
-    /**
-     * Gets filter configuration type
-     */
-    @FilterType
-    public abstract int getType();
-
-    public Settings getSettings() {
-        return mSettings;
-    }
-
-    // TODO: more builders and getters
-
-    /**
-     *  Filter configuration for a TS filter.
-     */
-    public static class TsFilterConfiguration extends FilterConfiguration {
-        private int mTpid;
-
-        private TsFilterConfiguration(Settings settings, int tpid) {
-            super(settings);
-            mTpid = tpid;
-        }
-
-        @Override
-        public int getType() {
-            return TunerConstants.FILTER_TYPE_TS;
-        }
-
-        /**
-         * Creates a new builder.
-         */
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        /**
-         * Builder for TsFilterConfiguration.
-         */
-        public static class Builder {
-            private Settings mSettings;
-            private int mTpid;
-
-            /**
-             * Sets settings.
-             */
-            public Builder setSettings(Settings settings) {
-                mSettings = settings;
-                return this;
-            }
-
-            /**
-             * Sets TPID.
-             */
-            public Builder setTpid(int tpid) {
-                mTpid = tpid;
-                return this;
-            }
-
-            /**
-             * Builds a TsFilterConfiguration instance.
-             */
-            public TsFilterConfiguration build() {
-                return new TsFilterConfiguration(mSettings, mTpid);
-            }
-        }
-    }
-
-    /**
-     *  Filter configuration for a MMTP filter.
-     */
-    public static class MmtpFilterConfiguration extends FilterConfiguration {
-        private int mMmtpPid;
-
-        public MmtpFilterConfiguration(Settings settings) {
-            super(settings);
-        }
-
-        @Override
-        public int getType() {
-            return TunerConstants.FILTER_TYPE_MMTP;
-        }
-    }
-
-
-    /**
-     *  Filter configuration for a IP filter.
-     */
-    public static class IpFilterConfiguration extends FilterConfiguration {
-        private byte[] mSrcIpAddress;
-        private byte[] mDstIpAddress;
-        private int mSrcPort;
-        private int mDstPort;
-        private boolean mPassthrough;
-
-        public IpFilterConfiguration(Settings settings) {
-            super(settings);
-        }
-
-        @Override
-        public int getType() {
-            return TunerConstants.FILTER_TYPE_IP;
-        }
-    }
-
-
-    /**
-     *  Filter configuration for a TLV filter.
-     */
-    public static class TlvFilterConfiguration extends FilterConfiguration {
-        private int mPacketType;
-        private boolean mIsCompressedIpPacket;
-        private boolean mPassthrough;
-
-        public TlvFilterConfiguration(Settings settings) {
-            super(settings);
-        }
-
-        @Override
-        public int getType() {
-            return TunerConstants.FILTER_TYPE_TLV;
-        }
-    }
-
-
-    /**
-     *  Filter configuration for a ALP filter.
-     */
-    public static class AlpFilterConfiguration extends FilterConfiguration {
-        private int mPacketType;
-        private int mLengthType;
-
-        public AlpFilterConfiguration(Settings settings) {
-            super(settings);
-        }
-
-        @Override
-        public int getType() {
-            return TunerConstants.FILTER_TYPE_ALP;
-        }
-    }
-
-
-    /**
-     *  Settings for filters of different subtypes.
-     */
-    public abstract static class Settings {
-        protected final int mType;
-
-        protected Settings(int type) {
-            mType = type;
-        }
-
-        /**
-         * Gets filter settings type.
-         * @return
-         */
-        int getType() {
-            return mType;
-        }
-    }
-
-    /**
-     *  Filter Settings for Section data according to ISO/IEC 13818-1.
-     */
-    public static class SectionSettings extends Settings {
-
-        private SectionSettings(int mainType) {
-            super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_SECTION));
-        }
-    }
-
-    /**
-     *  Bits Settings for Section Filter.
-     */
-    public static class SectionSettingsWithSectionBits extends SectionSettings {
-        private List<Byte> mFilter;
-        private List<Byte> mMask;
-        private List<Byte> mMode;
-
-        private SectionSettingsWithSectionBits(int mainType) {
-            super(mainType);
-        }
-    }
-
-    /**
-     *  Table information for Section Filter.
-     */
-    public static class SectionSettingsWithTableInfo extends SectionSettings {
-        private int mTableId;
-        private int mVersion;
-
-        private SectionSettingsWithTableInfo(int mainType) {
-            super(mainType);
-        }
-    }
-
-    /**
-     *  Filter Settings for a PES Data.
-     */
-    public static class PesSettings extends Settings {
-        private int mStreamId;
-        private boolean mIsRaw;
-
-        private PesSettings(int mainType, int streamId, boolean isRaw) {
-            super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_PES));
-            mStreamId = streamId;
-            mIsRaw = isRaw;
-        }
-
-        /**
-         * Creates a builder for PesSettings.
-         */
-        public static Builder newBuilder(int mainType) {
-            return new Builder(mainType);
-        }
-
-        /**
-         * Builder for PesSettings.
-         */
-        public static class Builder {
-            private final int mMainType;
-            private int mStreamId;
-            private boolean mIsRaw;
-
-            public Builder(int mainType) {
-                mMainType = mainType;
-            }
-
-            /**
-             * Sets stream ID.
-             */
-            public Builder setStreamId(int streamId) {
-                mStreamId = streamId;
-                return this;
-            }
-
-            /**
-             * Sets whether it's raw.
-             * true if the filter send onFilterStatus instead of onFilterEvent.
-             */
-            public Builder setIsRaw(boolean isRaw) {
-                mIsRaw = isRaw;
-                return this;
-            }
-
-            /**
-             * Builds a PesSettings instance.
-             */
-            public PesSettings build() {
-                return new PesSettings(mMainType, mStreamId, mIsRaw);
-            }
-        }
-    }
-
-    /**
-     *  Filter Settings for a Video and Audio.
-     */
-    public static class AvSettings extends Settings {
-        private boolean mIsPassthrough;
-
-        private AvSettings(int mainType, boolean isAudio) {
-            super(TunerUtils.getFilterSubtype(
-                    mainType,
-                    isAudio
-                            ? TunerConstants.FILTER_SUBTYPE_AUDIO
-                            : TunerConstants.FILTER_SUBTYPE_VIDEO));
-        }
-    }
-
-    /**
-     *  Filter Settings for a Download.
-     */
-    public static class DownloadSettings extends Settings {
-        private int mDownloadId;
-
-        public DownloadSettings(int mainType) {
-            super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_DOWNLOAD));
-        }
-    }
-
-    /**
-     *  The Settings for the record in DVR.
-     */
-    public static class RecordSettings extends Settings {
-        private int mIndexType;
-        private int mIndexMask;
-
-        public RecordSettings(int mainType) {
-            super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_RECORD));
-        }
-    }
-
-}
diff --git a/media/java/android/media/tv/tuner/FrontendCapabilities.java b/media/java/android/media/tv/tuner/FrontendCapabilities.java
deleted file mode 100644
index fcfd7c8..0000000
--- a/media/java/android/media/tv/tuner/FrontendCapabilities.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * 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.
- */
-
-package android.media.tv.tuner;
-
-/**
- * Frontend Capabilities.
- * @hide
- */
-public class FrontendCapabilities {
-    /** Analog Capabilities. */
-    public class Analog extends FrontendCapabilities {
-        private final int mTypeCap;
-        private final int mSifStandardCap;
-
-        Analog(int typeCap, int sifStandardCap) {
-            mTypeCap = typeCap;
-            mSifStandardCap = sifStandardCap;
-        }
-        /**
-         * Gets type capability.
-         */
-        public int getTypeCapability() {
-            return mTypeCap;
-        }
-        /** Gets SIF standard capability. */
-        public int getSifStandardCapability() {
-            return mSifStandardCap;
-        }
-    }
-
-    /** ATSC Capabilities. */
-    public class Atsc extends FrontendCapabilities {
-        private final int mModulationCap;
-
-        Atsc(int modulationCap) {
-            mModulationCap = modulationCap;
-        }
-        /** Gets modulation capability. */
-        public int getModulationCapability() {
-            return mModulationCap;
-        }
-    }
-
-    /** ATSC-3 Capabilities. */
-    public class Atsc3 extends FrontendCapabilities {
-        private final int mBandwidthCap;
-        private final int mModulationCap;
-        private final int mTimeInterleaveModeCap;
-        private final int mCodeRateCap;
-        private final int mFecCap;
-        private final int mDemodOutputFormatCap;
-
-        Atsc3(int bandwidthCap, int modulationCap, int timeInterleaveModeCap, int codeRateCap,
-                int fecCap, int demodOutputFormatCap) {
-            mBandwidthCap = bandwidthCap;
-            mModulationCap = modulationCap;
-            mTimeInterleaveModeCap = timeInterleaveModeCap;
-            mCodeRateCap = codeRateCap;
-            mFecCap = fecCap;
-            mDemodOutputFormatCap = demodOutputFormatCap;
-        }
-
-        /** Gets bandwidth capability. */
-        public int getBandwidthCapability() {
-            return mBandwidthCap;
-        }
-        /** Gets modulation capability. */
-        public int getModulationCapability() {
-            return mModulationCap;
-        }
-        /** Gets time interleave mod capability. */
-        public int getTimeInterleaveModeCapability() {
-            return mTimeInterleaveModeCap;
-        }
-        /** Gets code rate capability. */
-        public int getCodeRateCapability() {
-            return mCodeRateCap;
-        }
-        /** Gets FEC capability. */
-        public int getFecCapability() {
-            return mFecCap;
-        }
-        /** Gets demodulator output format capability. */
-        public int getDemodOutputFormatCapability() {
-            return mDemodOutputFormatCap;
-        }
-    }
-
-    /** DVBS Capabilities. */
-    public class Dvbs extends FrontendCapabilities {
-        private final int mModulationCap;
-        private final long mInnerFecCap;
-        private final int mStandard;
-
-        Dvbs(int modulationCap, long innerFecCap, int standard) {
-            mModulationCap = modulationCap;
-            mInnerFecCap = innerFecCap;
-            mStandard = standard;
-        }
-
-        /** Gets modulation capability. */
-        public int getModulationCapability() {
-            return mModulationCap;
-        }
-        /** Gets inner FEC capability. */
-        public long getInnerFecCapability() {
-            return mInnerFecCap;
-        }
-        /** Gets DVBS standard capability. */
-        public int getStandardCapability() {
-            return mStandard;
-        }
-    }
-
-    /** DVBC Capabilities. */
-    public class Dvbc extends FrontendCapabilities {
-        private final int mModulationCap;
-        private final int mFecCap;
-        private final int mAnnexCap;
-
-        Dvbc(int modulationCap, int fecCap, int annexCap) {
-            mModulationCap = modulationCap;
-            mFecCap = fecCap;
-            mAnnexCap = annexCap;
-        }
-
-        /** Gets modulation capability. */
-        public int getModulationCapability() {
-            return mModulationCap;
-        }
-        /** Gets FEC capability. */
-        public int getFecCapability() {
-            return mFecCap;
-        }
-        /** Gets annex capability. */
-        public int getAnnexCapability() {
-            return mAnnexCap;
-        }
-    }
-
-    /** DVBT Capabilities. */
-    public class Dvbt extends FrontendCapabilities {
-        private final int mTransmissionModeCap;
-        private final int mBandwidthCap;
-        private final int mConstellationCap;
-        private final int mCoderateCap;
-        private final int mHierarchyCap;
-        private final int mGuardIntervalCap;
-        private final boolean mIsT2Supported;
-        private final boolean mIsMisoSupported;
-
-        Dvbt(int transmissionModeCap, int bandwidthCap, int constellationCap, int coderateCap,
-                int hierarchyCap, int guardIntervalCap, boolean isT2Supported,
-                boolean isMisoSupported) {
-            mTransmissionModeCap = transmissionModeCap;
-            mBandwidthCap = bandwidthCap;
-            mConstellationCap = constellationCap;
-            mCoderateCap = coderateCap;
-            mHierarchyCap = hierarchyCap;
-            mGuardIntervalCap = guardIntervalCap;
-            mIsT2Supported = isT2Supported;
-            mIsMisoSupported = isMisoSupported;
-        }
-
-        /** Gets transmission mode capability. */
-        public int getTransmissionModeCapability() {
-            return mTransmissionModeCap;
-        }
-        /** Gets bandwidth capability. */
-        public int getBandwidthCapability() {
-            return mBandwidthCap;
-        }
-        /** Gets constellation capability. */
-        public int getConstellationCapability() {
-            return mConstellationCap;
-        }
-        /** Gets code rate capability. */
-        public int getCodeRateCapability() {
-            return mCoderateCap;
-        }
-        /** Gets hierarchy capability. */
-        public int getHierarchyCapability() {
-            return mHierarchyCap;
-        }
-        /** Gets guard interval capability. */
-        public int getGuardIntervalCapability() {
-            return mGuardIntervalCap;
-        }
-        /** Returns whether T2 is supported. */
-        public boolean getIsT2Supported() {
-            return mIsT2Supported;
-        }
-        /** Returns whether MISO is supported. */
-        public boolean getIsMisoSupported() {
-            return mIsMisoSupported;
-        }
-    }
-
-    /** ISDBS Capabilities. */
-    public class Isdbs extends FrontendCapabilities {
-        private final int mModulationCap;
-        private final int mCoderateCap;
-
-        Isdbs(int modulationCap, int coderateCap) {
-            mModulationCap = modulationCap;
-            mCoderateCap = coderateCap;
-        }
-
-        /** Gets modulation capability. */
-        public int getModulationCapability() {
-            return mModulationCap;
-        }
-        /** Gets code rate capability. */
-        public int getCodeRateCapability() {
-            return mCoderateCap;
-        }
-    }
-
-    /** ISDBS-3 Capabilities. */
-    public class Isdbs3 extends FrontendCapabilities {
-        private final int mModulationCap;
-        private final int mCoderateCap;
-
-        Isdbs3(int modulationCap, int coderateCap) {
-            mModulationCap = modulationCap;
-            mCoderateCap = coderateCap;
-        }
-
-        /** Gets modulation capability. */
-        public int getModulationCapability() {
-            return mModulationCap;
-        }
-        /** Gets code rate capability. */
-        public int getCodeRateCapability() {
-            return mCoderateCap;
-        }
-    }
-
-    /** ISDBC Capabilities. */
-    public class Isdbc extends FrontendCapabilities {
-        private final int mModeCap;
-        private final int mBandwidthCap;
-        private final int mModulationCap;
-        private final int mCoderateCap;
-        private final int mGuardIntervalCap;
-
-        Isdbc(int modeCap, int bandwidthCap, int modulationCap, int coderateCap,
-                int guardIntervalCap) {
-            mModeCap = modeCap;
-            mBandwidthCap = bandwidthCap;
-            mModulationCap = modulationCap;
-            mCoderateCap = coderateCap;
-            mGuardIntervalCap = guardIntervalCap;
-        }
-
-        /** Gets mode capability. */
-        public int getModeCapability() {
-            return mModeCap;
-        }
-        /** Gets bandwidth capability. */
-        public int getBandwidthCapability() {
-            return mBandwidthCap;
-        }
-        /** Gets modulation capability. */
-        public int getModulationCapability() {
-            return mModulationCap;
-        }
-        /** Gets code rate capability. */
-        public int getCodeRateCapability() {
-            return mCoderateCap;
-        }
-        /** Gets guard interval capability. */
-        public int getGuardIntervalCapability() {
-            return mGuardIntervalCap;
-        }
-    }
-}
diff --git a/media/java/android/media/tv/tuner/TimeFilter.java b/media/java/android/media/tv/tuner/TimeFilter.java
new file mode 100644
index 0000000..8bd0d26
--- /dev/null
+++ b/media/java/android/media/tv/tuner/TimeFilter.java
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner;
+
+import android.annotation.Nullable;
+import android.media.tv.tuner.TunerConstants.Result;
+
+/**
+ *  A timer filter is used to filter data based on timestamps.
+ *
+ *  <p> If the timestamp is set, data is discarded if its timestamp is smaller than the
+ *  timestamp in this time filter.
+ *
+ *  <p> The format of the timestamps is the same as PTS defined in ISO/IEC 13818-1:2019. The
+ *  timestamps may or may not be related to PTS or DTS.
+ *
+ * @hide
+ */
+public class TimeFilter implements AutoCloseable {
+    private native int nativeSetTimestamp(long timestamp);
+    private native int nativeClearTimestamp();
+    private native Long nativeGetTimestamp();
+    private native Long nativeGetSourceTime();
+    private native int nativeClose();
+
+    private boolean mEnable = false;
+
+    /**
+     * Set timestamp for time based filter.
+     *
+     * It is used to set initial timestamp and enable time filtering. Once set, the time will be
+     * increased automatically like a clock. Contents are discarded if their timestamps are
+     * older than the time in the time filter.
+     *
+     * This method can be called more than once to reset the initial timestamp.
+     *
+     * @param timestamp initial timestamp for the time filter before it's increased. It's
+     * based on the 90KHz counter, and it's the same format as PTS (Presentation Time Stamp)
+     * defined in ISO/IEC 13818-1:2019. The timestamps may or may not be related to PTS or DTS.
+     * @return result status of the operation.
+     */
+    @Result
+    public int setCurrentTimestamp(long timestamp) {
+        int res = nativeSetTimestamp(timestamp);
+        // TODO: use a constant for SUCCESS
+        if (res == 0) {
+            mEnable = true;
+        }
+        return res;
+    }
+
+    /**
+     * Clear the timestamp in the time filter.
+     *
+     * It is used to clear the time value of the time filter. Time filtering is disabled then.
+     *
+     * @return result status of the operation.
+     */
+    @Result
+    public int clearTimestamp() {
+        int res = nativeClearTimestamp();
+        if (res == 0) {
+            mEnable = false;
+        }
+        return res;
+    }
+
+    /**
+     * Get the current time in the time filter.
+     *
+     * It is used to inquiry current time in the time filter.
+     *
+     * @return current timestamp in the time filter. It's based on the 90KHz counter, and it's
+     * the same format as PTS (Presentation Time Stamp) defined in ISO/IEC 13818-1:2019. The
+     * timestamps may or may not be related to PTS or DTS. {@code null} if the timestamp is
+     * never set.
+     */
+    @Nullable
+    public Long getTimeStamp() {
+        if (!mEnable) {
+            return null;
+        }
+        return nativeGetTimestamp();
+    }
+
+    /**
+     * Get the timestamp from the beginning of incoming data stream.
+     *
+     * It is used to inquiry the timestamp from the beginning of incoming data stream.
+     *
+     * @return first timestamp of incoming data stream. It's based on the 90KHz counter, and
+     * it's the same format as PTS (Presentation Time Stamp) defined in ISO/IEC 13818-1:2019.
+     * The timestamps may or may not be related to PTS or DTS.
+     */
+    @Nullable
+    public Long getSourceTime() {
+        if (!mEnable) {
+            return null;
+        }
+        return nativeGetSourceTime();
+    }
+
+    /**
+     * Close the Time Filter instance
+     *
+     * It is to release the TimeFilter instance. Resources are reclaimed so the instance must
+     * not be accessed after this method is called.
+     */
+    @Override
+    public void close() {
+        nativeClose();
+    }
+}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 43f9a89..b3b2ba1 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -21,8 +21,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
-import android.media.tv.tuner.FilterConfiguration.Settings;
-import android.media.tv.tuner.TunerConstants.DemuxPidType;
 import android.media.tv.tuner.TunerConstants.FilterStatus;
 import android.media.tv.tuner.TunerConstants.FilterSubtype;
 import android.media.tv.tuner.TunerConstants.FilterType;
@@ -39,7 +37,6 @@
 import android.os.Looper;
 import android.os.Message;
 
-import java.io.FileDescriptor;
 import java.util.List;
 
 /**
@@ -123,6 +120,7 @@
     private native int nativeDisconnectCiCam();
     private native FrontendInfo nativeGetFrontendInfo(int id);
     private native Filter nativeOpenFilter(int type, int subType, int bufferSize);
+    private native TimeFilter nativeOpenTimeFilter();
 
     private native List<Integer> nativeGetLnbIds();
     private native Lnb nativeOpenLnbById(int id);
@@ -456,122 +454,11 @@
      * Tuner data filter.
      *
      * <p> This class is used to filter wanted data according to the filter's configuration.
+     * TODO: remove
      */
     public class Filter {
-        private long mNativeContext;
-        private FilterCallback mCallback;
-        int mId;
-
-        private native int nativeConfigureFilter(
-                int type, int subType, FilterConfiguration settings);
-        private native int nativeGetId();
-        private native int nativeSetDataSource(Filter source);
-        private native int nativeStartFilter();
-        private native int nativeStopFilter();
-        private native int nativeFlushFilter();
-        private native int nativeRead(byte[] buffer, int offset, int size);
-        private native int nativeClose();
-
-        private Filter(int id) {
-            mId = id;
-        }
-
-        private void onFilterStatus(int status) {
-            if (mHandler != null) {
-                mHandler.sendMessage(
-                        mHandler.obtainMessage(MSG_ON_FILTER_STATUS, status, 0, this));
-            }
-        }
-
-        /**
-         * Configures the filter.
-         *
-         * @param settings the settings of the filter.
-         * @return result status of the operation.
-         * @hide
-         */
-        public int configure(FilterConfiguration settings) {
-            int subType = -1;
-            Settings s = settings.getSettings();
-            if (s != null) {
-                subType = s.getType();
-            }
-            return nativeConfigureFilter(settings.getType(), subType, settings);
-        }
-
-        /**
-         * Gets the filter Id.
-         *
-         * @return the hardware resource Id for the filter.
-         * @hide
-         */
-        public int getId() {
-            return nativeGetId();
-        }
-
-        /**
-         * Sets the filter's data source.
-         *
-         * A filter uses demux as data source by default. If the data was packetized
-         * by multiple protocols, multiple filters may need to work together to
-         * extract all protocols' header. Then a filter's data source can be output
-         * from another filter.
-         *
-         * @param source the filter instance which provides data input. Switch to
-         * use demux as data source if the filter instance is NULL.
-         * @return result status of the operation.
-         * @hide
-         */
-        public int setDataSource(@Nullable Filter source) {
-            return nativeSetDataSource(source);
-        }
-
-        /**
-         * Starts the filter.
-         *
-         * @return result status of the operation.
-         * @hide
-         */
-        public int start() {
-            return nativeStartFilter();
-        }
-
-
-        /**
-         * Stops the filter.
-         *
-         * @return result status of the operation.
-         * @hide
-         */
-        public int stop() {
-            return nativeStopFilter();
-        }
-
-        /**
-         * Flushes the filter.
-         *
-         * @return result status of the operation.
-         * @hide
-         */
-        public int flush() {
-            return nativeFlushFilter();
-        }
-
-        /** @hide */
-        public int read(@NonNull byte[] buffer, int offset, int size) {
-            size = Math.min(size, buffer.length - offset);
-            return nativeRead(buffer, offset, size);
-        }
-
-        /**
-         * Release the Filter instance.
-         *
-         * @return result status of the operation.
-         * @hide
-         */
-        public int close() {
-            return nativeClose();
-        }
+        FilterCallback mCallback;
+        private Filter() {}
     }
 
     private Filter openFilter(@FilterType int mainType, @FilterSubtype int subType, int bufferSize,
@@ -587,6 +474,18 @@
         return filter;
     }
 
+    /**
+     * Open a time filter instance.
+     *
+     * It is used to open time filter of demux.
+     *
+     * @return a time filter instance.
+     * @hide
+     */
+    public TimeFilter openTimeFilter() {
+        return nativeOpenTimeFilter();
+    }
+
     /** @hide */
     public class Lnb {
         private int mId;
@@ -699,81 +598,11 @@
      * <p> Descrambler is a hardware component used to descramble data.
      *
      * <p> This class controls the TIS interaction with Tuner HAL.
-     * TODO: make it static and extends Closable.
+     * TODO: Remove
      */
     public class Descrambler {
-        private long mNativeContext;
-
-        private native int nativeAddPid(int pidType, int pid, Filter filter);
-        private native int nativeRemovePid(int pidType, int pid, Filter filter);
-        private native int nativeSetKeyToken(byte[] keyToken);
-        private native int nativeClose();
-
-        private Descrambler() {}
-
-        /**
-         * Add packets' PID to the descrambler for descrambling.
-         *
-         * The descrambler will start descrambling packets with this PID. Multiple PIDs can be added
-         * into one descrambler instance because descambling can happen simultaneously on packets
-         * from different PIDs.
-         *
-         * If the Descrambler previously contained a filter for the PID, the old filter is replaced
-         * by the specified filter.
-         *
-         * @param pidType the type of the PID.
-         * @param pid the PID of packets to start to be descrambled.
-         * @param filter an optional filter instance to identify upper stream.
-         * @return result status of the operation.
-         *
-         * @hide
-         */
-        public int addPid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) {
-            return nativeAddPid(pidType, pid, filter);
+        private Descrambler() {
         }
-
-        /**
-         * Remove packets' PID from the descrambler
-         *
-         * The descrambler will stop descrambling packets with this PID.
-         *
-         * @param pidType the type of the PID.
-         * @param pid the PID of packets to stop to be descrambled.
-         * @param filter an optional filter instance to identify upper stream.
-         * @return result status of the operation.
-         *
-         * @hide
-         */
-        public int removePid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) {
-            return nativeRemovePid(pidType, pid, filter);
-        }
-
-        /**
-         * Set a key token to link descrambler to a key slot
-         *
-         * A descrambler instance can have only one key slot to link, but a key slot can hold a few
-         * keys for different purposes.
-         *
-         * @param keyToken the token to be used to link the key slot.
-         * @return result status of the operation.
-         *
-         * @hide
-         */
-        public int setKeyToken(byte[] keyToken) {
-            return nativeSetKeyToken(keyToken);
-        }
-
-        /**
-         * Release the descrambler instance.
-         *
-         * @return result status of the operation.
-         *
-         * @hide
-         */
-        public int close() {
-            return nativeClose();
-        }
-
     }
 
     /**
@@ -788,137 +617,6 @@
         return nativeOpenDescrambler();
     }
 
-    // TODO: consider splitting Dvr to Playback and Recording
-    /** @hide */
-    public class Dvr {
-        private long mNativeContext;
-        private DvrCallback mCallback;
-
-        private native int nativeAttachFilter(Filter filter);
-        private native int nativeDetachFilter(Filter filter);
-        private native int nativeConfigureDvr(DvrSettings settings);
-        private native int nativeStartDvr();
-        private native int nativeStopDvr();
-        private native int nativeFlushDvr();
-        private native int nativeClose();
-        private native void nativeSetFileDescriptor(FileDescriptor fd);
-        private native int nativeRead(int size);
-        private native int nativeRead(byte[] bytes, int offset, int size);
-        private native int nativeWrite(int size);
-        private native int nativeWrite(byte[] bytes, int offset, int size);
-
-        private Dvr() {}
-
-        /**
-         * Attaches a filter to DVR interface for recording.
-         *
-         * @param filter the filter to be attached.
-         * @return result status of the operation.
-         */
-        public int attachFilter(Filter filter) {
-            return nativeAttachFilter(filter);
-        }
-
-        /**
-         * Detaches a filter from DVR interface.
-         *
-         * @param filter the filter to be detached.
-         * @return result status of the operation.
-         */
-        public int detachFilter(Filter filter) {
-            return nativeDetachFilter(filter);
-        }
-
-        /**
-         * Configures the DVR.
-         *
-         * @param settings the settings of the DVR interface.
-         * @return result status of the operation.
-         */
-        public int configure(DvrSettings settings) {
-            return nativeConfigureDvr(settings);
-        }
-
-        /**
-         * Starts DVR.
-         *
-         * Starts consuming playback data or producing data for recording.
-         *
-         * @return result status of the operation.
-         */
-        public int start() {
-            return nativeStartDvr();
-        }
-
-        /**
-         * Stops DVR.
-         *
-         * Stops consuming playback data or producing data for recording.
-         *
-         * @return result status of the operation.
-         */
-        public int stop() {
-            return nativeStopDvr();
-        }
-
-        /**
-         * Flushed DVR data.
-         *
-         * @return result status of the operation.
-         */
-        public int flush() {
-            return nativeFlushDvr();
-        }
-
-        /**
-         * closes the DVR instance to release resources.
-         *
-         * @return result status of the operation.
-         */
-        public int close() {
-            return nativeClose();
-        }
-
-        /**
-         * Sets file descriptor to read/write data.
-         */
-        public void setFileDescriptor(FileDescriptor fd) {
-            nativeSetFileDescriptor(fd);
-        }
-
-        /**
-         * Reads data from the file for DVR playback.
-         */
-        public int read(int size) {
-            return nativeRead(size);
-        }
-
-        /**
-         * Reads data from the buffer for DVR playback.
-         */
-        public int read(@NonNull byte[] bytes, int offset, int size) {
-            if (size + offset > bytes.length) {
-                throw new ArrayIndexOutOfBoundsException(
-                        "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
-            }
-            return nativeRead(bytes, offset, size);
-        }
-
-        /**
-         * Writes recording data to file.
-         */
-        public int write(int size) {
-            return nativeWrite(size);
-        }
-
-        /**
-         * Writes recording data to buffer.
-         */
-        public int write(@NonNull byte[] bytes, int offset, int size) {
-            return nativeWrite(bytes, offset, size);
-        }
-    }
-
     private Dvr openDvr(int type, int bufferSize) {
         Dvr dvr = nativeOpenDvr(type, bufferSize);
         return dvr;
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index d24e582..e024432 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -227,41 +227,6 @@
     /** @hide */
     public static final int FRONTEND_SCAN_BLIND = Constants.FrontendScanType.SCAN_BLIND;
 
-    /** @hide */
-    @IntDef({SCAN_MESSAGE_TYPE_LOCKED, SCAN_MESSAGE_TYPE_END, SCAN_MESSAGE_TYPE_PROGRESS_PERCENT,
-            SCAN_MESSAGE_TYPE_FREQUENCY, SCAN_MESSAGE_TYPE_SYMBOL_RATE, SCAN_MESSAGE_TYPE_PLP_IDS,
-            SCAN_MESSAGE_TYPE_GROUP_IDS, SCAN_MESSAGE_TYPE_INPUT_STREAM_IDS,
-            SCAN_MESSAGE_TYPE_STANDARD, SCAN_MESSAGE_TYPE_ATSC3_PLP_INFO})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ScanMessageType {}
-    /** @hide */
-    public static final int SCAN_MESSAGE_TYPE_LOCKED = Constants.FrontendScanMessageType.LOCKED;
-    /** @hide */
-    public static final int SCAN_MESSAGE_TYPE_END = Constants.FrontendScanMessageType.END;
-    /** @hide */
-    public static final int SCAN_MESSAGE_TYPE_PROGRESS_PERCENT =
-            Constants.FrontendScanMessageType.PROGRESS_PERCENT;
-    /** @hide */
-    public static final int SCAN_MESSAGE_TYPE_FREQUENCY =
-            Constants.FrontendScanMessageType.FREQUENCY;
-    /** @hide */
-    public static final int SCAN_MESSAGE_TYPE_SYMBOL_RATE =
-            Constants.FrontendScanMessageType.SYMBOL_RATE;
-    /** @hide */
-    public static final int SCAN_MESSAGE_TYPE_PLP_IDS = Constants.FrontendScanMessageType.PLP_IDS;
-    /** @hide */
-    public static final int SCAN_MESSAGE_TYPE_GROUP_IDS =
-            Constants.FrontendScanMessageType.GROUP_IDS;
-    /** @hide */
-    public static final int SCAN_MESSAGE_TYPE_INPUT_STREAM_IDS =
-            Constants.FrontendScanMessageType.INPUT_STREAM_IDS;
-    /** @hide */
-    public static final int SCAN_MESSAGE_TYPE_STANDARD =
-            Constants.FrontendScanMessageType.STANDARD;
-    /** @hide */
-    public static final int SCAN_MESSAGE_TYPE_ATSC3_PLP_INFO =
-            Constants.FrontendScanMessageType.ATSC3_PLP_INFO;
-
 
     /** @hide */
     @IntDef({FRONTEND_STATUS_TYPE_DEMOD_LOCK, FRONTEND_STATUS_TYPE_SNR, FRONTEND_STATUS_TYPE_BER,
diff --git a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
new file mode 100644
index 0000000..4d02b84
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Filter configuration for a ALP filter.
+ * @hide
+ */
+public class AlpFilterConfiguration extends FilterConfiguration {
+    private int mPacketType;
+    private int mLengthType;
+
+    public AlpFilterConfiguration(Settings settings) {
+        super(settings);
+    }
+
+    @Override
+    public int getType() {
+        return TunerConstants.FILTER_TYPE_ALP;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/AvSettings.java b/media/java/android/media/tv/tuner/filter/AvSettings.java
new file mode 100644
index 0000000..a7c49d5
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/AvSettings.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.media.tv.tuner.TunerConstants;
+import android.media.tv.tuner.TunerUtils;
+
+/**
+ * Filter Settings for a Video and Audio.
+ * @hide
+ */
+public class AvSettings extends Settings {
+    private boolean mIsPassthrough;
+
+    private AvSettings(int mainType, boolean isAudio) {
+        super(TunerUtils.getFilterSubtype(
+                mainType,
+                isAudio
+                        ? TunerConstants.FILTER_SUBTYPE_AUDIO
+                        : TunerConstants.FILTER_SUBTYPE_VIDEO));
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/DownloadSettings.java b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
new file mode 100644
index 0000000..0742b11
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.media.tv.tuner.TunerConstants;
+import android.media.tv.tuner.TunerUtils;
+
+/**
+ * Filter Settings for a Download.
+ * @hide
+ */
+public class DownloadSettings extends Settings {
+    private int mDownloadId;
+
+    public DownloadSettings(int mainType) {
+        super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_DOWNLOAD));
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
new file mode 100644
index 0000000..930ca74
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.annotation.Nullable;
+import android.media.tv.tuner.TunerConstants.FilterType;
+
+/**
+ * Demux Filter configuration.
+ *
+ * @hide
+ */
+public abstract class FilterConfiguration {
+    @Nullable
+    protected final Settings mSettings;
+
+    protected FilterConfiguration(Settings settings) {
+        mSettings = settings;
+    }
+
+    /**
+     * Gets filter configuration type
+     */
+    @FilterType
+    public abstract int getType();
+
+    public Settings getSettings() {
+        return mSettings;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
new file mode 100644
index 0000000..2c706c0
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Filter configuration for a IP filter.
+ * @hide
+ */
+public class IpFilterConfiguration extends FilterConfiguration {
+    private byte[] mSrcIpAddress;
+    private byte[] mDstIpAddress;
+    private int mSrcPort;
+    private int mDstPort;
+    private boolean mPassthrough;
+
+    public IpFilterConfiguration(Settings settings) {
+        super(settings);
+    }
+
+    @Override
+    public int getType() {
+        return TunerConstants.FILTER_TYPE_IP;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
new file mode 100644
index 0000000..f70e70a
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Filter configuration for a MMTP filter.
+ * @hide
+ */
+public class MmtpFilterConfiguration extends FilterConfiguration {
+    private int mMmtpPid;
+
+    public MmtpFilterConfiguration(Settings settings) {
+        super(settings);
+    }
+
+    @Override
+    public int getType() {
+        return TunerConstants.FILTER_TYPE_MMTP;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/PesSettings.java b/media/java/android/media/tv/tuner/filter/PesSettings.java
new file mode 100644
index 0000000..3d2c265
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/PesSettings.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.media.tv.tuner.TunerConstants;
+import android.media.tv.tuner.TunerUtils;
+
+/**
+ * Filter Settings for a PES Data.
+ * @hide
+ */
+public class PesSettings extends Settings {
+    private int mStreamId;
+    private boolean mIsRaw;
+
+    private PesSettings(int mainType, int streamId, boolean isRaw) {
+        super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_PES));
+        mStreamId = streamId;
+        mIsRaw = isRaw;
+    }
+
+    /**
+     * Creates a builder for PesSettings.
+     */
+    public static Builder newBuilder(int mainType) {
+        return new Builder(mainType);
+    }
+
+    /**
+     * Builder for PesSettings.
+     */
+    public static class Builder {
+        private final int mMainType;
+        private int mStreamId;
+        private boolean mIsRaw;
+
+        public Builder(int mainType) {
+            mMainType = mainType;
+        }
+
+        /**
+         * Sets stream ID.
+         */
+        public Builder setStreamId(int streamId) {
+            mStreamId = streamId;
+            return this;
+        }
+
+        /**
+         * Sets whether it's raw.
+         * true if the filter send onFilterStatus instead of onFilterEvent.
+         */
+        public Builder setIsRaw(boolean isRaw) {
+            mIsRaw = isRaw;
+            return this;
+        }
+
+        /**
+         * Builds a PesSettings instance.
+         */
+        public PesSettings build() {
+            return new PesSettings(mMainType, mStreamId, mIsRaw);
+        }
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java
new file mode 100644
index 0000000..701868a
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.media.tv.tuner.TunerConstants;
+import android.media.tv.tuner.TunerUtils;
+
+/**
+ * The Settings for the record in DVR.
+ * @hide
+ */
+public class RecordSettings extends Settings {
+    private int mIndexType;
+    private int mIndexMask;
+
+    public RecordSettings(int mainType) {
+        super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_RECORD));
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java
new file mode 100644
index 0000000..36e3d7c
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.media.tv.tuner.TunerConstants;
+import android.media.tv.tuner.TunerUtils;
+
+/**
+ * Filter Settings for Section data according to ISO/IEC 13818-1.
+ * @hide
+ */
+public class SectionSettings extends Settings {
+
+    SectionSettings(int mainType) {
+        super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_SECTION));
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
new file mode 100644
index 0000000..414ea67
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+import java.util.List;
+
+/**
+ * Bits Settings for Section Filter.
+ * @hide
+ */
+public class SectionSettingsWithSectionBits extends SectionSettings {
+    private List<Byte> mFilter;
+    private List<Byte> mMask;
+    private List<Byte> mMode;
+
+    private SectionSettingsWithSectionBits(int mainType) {
+        super(mainType);
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
new file mode 100644
index 0000000..0df1d73
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+/**
+ * Table information for Section Filter.
+ * @hide
+ */
+public class SectionSettingsWithTableInfo extends SectionSettings {
+    private int mTableId;
+    private int mVersion;
+
+    private SectionSettingsWithTableInfo(int mainType) {
+        super(mainType);
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/Settings.java b/media/java/android/media/tv/tuner/filter/Settings.java
new file mode 100644
index 0000000..789ed08
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/Settings.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+/**
+ * Settings for filters of different subtypes.
+ * @hide
+ */
+public abstract class Settings {
+    protected final int mType;
+
+    protected Settings(int type) {
+        mType = type;
+    }
+
+    /**
+     * Gets filter settings type.
+     * @return
+     */
+    public int getType() {
+        return mType;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
new file mode 100644
index 0000000..1f8bcb3
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Filter configuration for a TLV filter.
+ * @hide
+ */
+public class TlvFilterConfiguration extends FilterConfiguration {
+    private int mPacketType;
+    private boolean mIsCompressedIpPacket;
+    private boolean mPassthrough;
+
+    public TlvFilterConfiguration(Settings settings) {
+        super(settings);
+    }
+
+    @Override
+    public int getType() {
+        return TunerConstants.FILTER_TYPE_TLV;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
new file mode 100644
index 0000000..103698c
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Filter configuration for a TS filter.
+ * @hide
+ */
+public class TsFilterConfiguration extends FilterConfiguration {
+    private int mTpid;
+
+    private TsFilterConfiguration(Settings settings, int tpid) {
+        super(settings);
+        mTpid = tpid;
+    }
+
+    @Override
+    public int getType() {
+        return TunerConstants.FILTER_TYPE_TS;
+    }
+
+    /**
+     * Creates a new builder.
+     */
+    public static TsFilterConfiguration.Builder newBuilder() {
+        return new TsFilterConfiguration.Builder();
+    }
+
+    /**
+     * Builder for TsFilterConfiguration.
+     */
+    public static class Builder {
+        private Settings mSettings;
+        private int mTpid;
+
+        /**
+         * Sets settings.
+         */
+        public TsFilterConfiguration.Builder setSettings(Settings settings) {
+            mSettings = settings;
+            return this;
+        }
+
+        /**
+         * Sets TPID.
+         */
+        public TsFilterConfiguration.Builder setTpid(int tpid) {
+            mTpid = tpid;
+            return this;
+        }
+
+        /**
+         * Builds a TsFilterConfiguration instance.
+         */
+        public TsFilterConfiguration build() {
+            return new TsFilterConfiguration(mSettings, mTpid);
+        }
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java
new file mode 100644
index 0000000..2962e98
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.frontend;
+
+/**
+ * Analog Capabilities.
+ * @hide
+ */
+public class AnalogFrontendCapabilities extends FrontendCapabilities {
+    private final int mTypeCap;
+    private final int mSifStandardCap;
+
+    AnalogFrontendCapabilities(int typeCap, int sifStandardCap) {
+        mTypeCap = typeCap;
+        mSifStandardCap = sifStandardCap;
+    }
+    /**
+     * Gets type capability.
+     */
+    public int getTypeCapability() {
+        return mTypeCap;
+    }
+    /** Gets SIF standard capability. */
+    public int getSifStandardCapability() {
+        return mSifStandardCap;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java
new file mode 100644
index 0000000..677f9387
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.frontend;
+
+/**
+ * ATSC-3 Capabilities.
+ * @hide
+ */
+public class Atsc3FrontendCapabilities extends FrontendCapabilities {
+    private final int mBandwidthCap;
+    private final int mModulationCap;
+    private final int mTimeInterleaveModeCap;
+    private final int mCodeRateCap;
+    private final int mFecCap;
+    private final int mDemodOutputFormatCap;
+
+    Atsc3FrontendCapabilities(int bandwidthCap, int modulationCap, int timeInterleaveModeCap,
+            int codeRateCap, int fecCap, int demodOutputFormatCap) {
+        mBandwidthCap = bandwidthCap;
+        mModulationCap = modulationCap;
+        mTimeInterleaveModeCap = timeInterleaveModeCap;
+        mCodeRateCap = codeRateCap;
+        mFecCap = fecCap;
+        mDemodOutputFormatCap = demodOutputFormatCap;
+    }
+
+    /** Gets bandwidth capability. */
+    public int getBandwidthCapability() {
+        return mBandwidthCap;
+    }
+    /** Gets modulation capability. */
+    public int getModulationCapability() {
+        return mModulationCap;
+    }
+    /** Gets time interleave mod capability. */
+    public int getTimeInterleaveModeCapability() {
+        return mTimeInterleaveModeCap;
+    }
+    /** Gets code rate capability. */
+    public int getCodeRateCapability() {
+        return mCodeRateCap;
+    }
+    /** Gets FEC capability. */
+    public int getFecCapability() {
+        return mFecCap;
+    }
+    /** Gets demodulator output format capability. */
+    public int getDemodOutputFormatCapability() {
+        return mDemodOutputFormatCap;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java
new file mode 100644
index 0000000..6ae3c63
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.frontend;
+
+/**
+ * ATSC Capabilities.
+ * @hide
+ */
+public class AtscFrontendCapabilities extends FrontendCapabilities {
+    private final int mModulationCap;
+
+    AtscFrontendCapabilities(int modulationCap) {
+        mModulationCap = modulationCap;
+    }
+    /** Gets modulation capability. */
+    public int getModulationCapability() {
+        return mModulationCap;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java
new file mode 100644
index 0000000..edea7af
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.frontend;
+
+/**
+ * DVBC Capabilities.
+ * @hide
+ */
+public class DvbcFrontendCapabilities extends FrontendCapabilities {
+    private final int mModulationCap;
+    private final int mFecCap;
+    private final int mAnnexCap;
+
+    DvbcFrontendCapabilities(int modulationCap, int fecCap, int annexCap) {
+        mModulationCap = modulationCap;
+        mFecCap = fecCap;
+        mAnnexCap = annexCap;
+    }
+
+    /** Gets modulation capability. */
+    public int getModulationCapability() {
+        return mModulationCap;
+    }
+    /** Gets FEC capability. */
+    public int getFecCapability() {
+        return mFecCap;
+    }
+    /** Gets annex capability. */
+    public int getAnnexCapability() {
+        return mAnnexCap;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java
new file mode 100644
index 0000000..f5a4157
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.frontend;
+
+/**
+ * DVBS Capabilities.
+ * @hide
+ */
+public class DvbsFrontendCapabilities extends FrontendCapabilities {
+    private final int mModulationCap;
+    private final long mInnerFecCap;
+    private final int mStandard;
+
+    DvbsFrontendCapabilities(int modulationCap, long innerFecCap, int standard) {
+        mModulationCap = modulationCap;
+        mInnerFecCap = innerFecCap;
+        mStandard = standard;
+    }
+
+    /** Gets modulation capability. */
+    public int getModulationCapability() {
+        return mModulationCap;
+    }
+    /** Gets inner FEC capability. */
+    public long getInnerFecCapability() {
+        return mInnerFecCap;
+    }
+    /** Gets DVBS standard capability. */
+    public int getStandardCapability() {
+        return mStandard;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java
new file mode 100644
index 0000000..e9c16ddd4
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.frontend;
+
+/**
+ * DVBT Capabilities.
+ * @hide
+ */
+public class DvbtFrontendCapabilities extends FrontendCapabilities {
+    private final int mTransmissionModeCap;
+    private final int mBandwidthCap;
+    private final int mConstellationCap;
+    private final int mCoderateCap;
+    private final int mHierarchyCap;
+    private final int mGuardIntervalCap;
+    private final boolean mIsT2Supported;
+    private final boolean mIsMisoSupported;
+
+    DvbtFrontendCapabilities(int transmissionModeCap, int bandwidthCap, int constellationCap,
+            int coderateCap, int hierarchyCap, int guardIntervalCap, boolean isT2Supported,
+            boolean isMisoSupported) {
+        mTransmissionModeCap = transmissionModeCap;
+        mBandwidthCap = bandwidthCap;
+        mConstellationCap = constellationCap;
+        mCoderateCap = coderateCap;
+        mHierarchyCap = hierarchyCap;
+        mGuardIntervalCap = guardIntervalCap;
+        mIsT2Supported = isT2Supported;
+        mIsMisoSupported = isMisoSupported;
+    }
+
+    /** Gets transmission mode capability. */
+    public int getTransmissionModeCapability() {
+        return mTransmissionModeCap;
+    }
+    /** Gets bandwidth capability. */
+    public int getBandwidthCapability() {
+        return mBandwidthCap;
+    }
+    /** Gets constellation capability. */
+    public int getConstellationCapability() {
+        return mConstellationCap;
+    }
+    /** Gets code rate capability. */
+    public int getCodeRateCapability() {
+        return mCoderateCap;
+    }
+    /** Gets hierarchy capability. */
+    public int getHierarchyCapability() {
+        return mHierarchyCap;
+    }
+    /** Gets guard interval capability. */
+    public int getGuardIntervalCapability() {
+        return mGuardIntervalCap;
+    }
+    /** Returns whether T2 is supported. */
+    public boolean getIsT2Supported() {
+        return mIsT2Supported;
+    }
+    /** Returns whether MISO is supported. */
+    public boolean getIsMisoSupported() {
+        return mIsMisoSupported;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java b/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
index 91776e1..0992eb6 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
@@ -16,8 +16,6 @@
 
 package android.media.tv.tuner.frontend;
 
-import android.media.tv.tuner.ScanMessage;
-
 /**
  * Frontend Callback.
  *
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java
new file mode 100644
index 0000000..7350bc0
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.frontend;
+
+/**
+ * Frontend Capabilities.
+ * @hide
+ */
+public abstract class FrontendCapabilities {
+}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
index ef6c029..5d03570 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
@@ -16,7 +16,6 @@
 
 package android.media.tv.tuner.frontend;
 
-import android.media.tv.tuner.FrontendCapabilities;
 import android.media.tv.tuner.TunerConstants.FrontendType;
 
 /**
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbcFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/IsdbcFrontendCapabilities.java
new file mode 100644
index 0000000..6544b17
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IsdbcFrontendCapabilities.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.frontend;
+
+/**
+ * ISDBC Capabilities.
+ * @hide
+ */
+public class IsdbcFrontendCapabilities extends FrontendCapabilities {
+    private final int mModeCap;
+    private final int mBandwidthCap;
+    private final int mModulationCap;
+    private final int mCoderateCap;
+    private final int mGuardIntervalCap;
+
+    IsdbcFrontendCapabilities(int modeCap, int bandwidthCap, int modulationCap, int coderateCap,
+            int guardIntervalCap) {
+        mModeCap = modeCap;
+        mBandwidthCap = bandwidthCap;
+        mModulationCap = modulationCap;
+        mCoderateCap = coderateCap;
+        mGuardIntervalCap = guardIntervalCap;
+    }
+
+    /** Gets mode capability. */
+    public int getModeCapability() {
+        return mModeCap;
+    }
+    /** Gets bandwidth capability. */
+    public int getBandwidthCapability() {
+        return mBandwidthCap;
+    }
+    /** Gets modulation capability. */
+    public int getModulationCapability() {
+        return mModulationCap;
+    }
+    /** Gets code rate capability. */
+    public int getCodeRateCapability() {
+        return mCoderateCap;
+    }
+    /** Gets guard interval capability. */
+    public int getGuardIntervalCapability() {
+        return mGuardIntervalCap;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java
new file mode 100644
index 0000000..92832b7
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.frontend;
+
+/**
+ * ISDBS-3 Capabilities.
+ * @hide
+ */
+public class Isdbs3FrontendCapabilities extends FrontendCapabilities {
+    private final int mModulationCap;
+    private final int mCoderateCap;
+
+    Isdbs3FrontendCapabilities(int modulationCap, int coderateCap) {
+        mModulationCap = modulationCap;
+        mCoderateCap = coderateCap;
+    }
+
+    /** Gets modulation capability. */
+    public int getModulationCapability() {
+        return mModulationCap;
+    }
+    /** Gets code rate capability. */
+    public int getCodeRateCapability() {
+        return mCoderateCap;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java
new file mode 100644
index 0000000..b930b25
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.tuner.frontend;
+
+/**
+ * ISDBS Capabilities.
+ * @hide
+ */
+public class IsdbsFrontendCapabilities extends FrontendCapabilities {
+    private final int mModulationCap;
+    private final int mCoderateCap;
+
+    IsdbsFrontendCapabilities(int modulationCap, int coderateCap) {
+        mModulationCap = modulationCap;
+        mCoderateCap = coderateCap;
+    }
+
+    /** Gets modulation capability. */
+    public int getModulationCapability() {
+        return mModulationCap;
+    }
+    /** Gets code rate capability. */
+    public int getCodeRateCapability() {
+        return mCoderateCap;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/ScanMessage.java b/media/java/android/media/tv/tuner/frontend/ScanMessage.java
similarity index 61%
rename from media/java/android/media/tv/tuner/ScanMessage.java
rename to media/java/android/media/tv/tuner/frontend/ScanMessage.java
index 35f54f8..dd687dd 100644
--- a/media/java/android/media/tv/tuner/ScanMessage.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanMessage.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,9 +14,13 @@
  * limitations under the License.
  */
 
-package android.media.tv.tuner;
+package android.media.tv.tuner.frontend;
 
-import android.media.tv.tuner.TunerConstants.ScanMessageType;
+import android.annotation.IntDef;
+import android.hardware.tv.tuner.V1_0.Constants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Message from frontend during scan operations.
@@ -24,6 +28,43 @@
  * @hide
  */
 public class ScanMessage {
+
+    /** @hide */
+    @IntDef({
+        LOCKED,
+        END,
+        PROGRESS_PERCENT,
+        FREQUENCY,
+        SYMBOL_RATE,
+        PLP_IDS,
+        GROUP_IDS,
+        INPUT_STREAM_IDS,
+        STANDARD,
+        ATSC3_PLP_INFO
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+    /** @hide */
+    public static final int LOCKED = Constants.FrontendScanMessageType.LOCKED;
+    /** @hide */
+    public static final int END = Constants.FrontendScanMessageType.END;
+    /** @hide */
+    public static final int PROGRESS_PERCENT = Constants.FrontendScanMessageType.PROGRESS_PERCENT;
+    /** @hide */
+    public static final int FREQUENCY = Constants.FrontendScanMessageType.FREQUENCY;
+    /** @hide */
+    public static final int SYMBOL_RATE = Constants.FrontendScanMessageType.SYMBOL_RATE;
+    /** @hide */
+    public static final int PLP_IDS = Constants.FrontendScanMessageType.PLP_IDS;
+    /** @hide */
+    public static final int GROUP_IDS = Constants.FrontendScanMessageType.GROUP_IDS;
+    /** @hide */
+    public static final int INPUT_STREAM_IDS = Constants.FrontendScanMessageType.INPUT_STREAM_IDS;
+    /** @hide */
+    public static final int STANDARD = Constants.FrontendScanMessageType.STANDARD;
+    /** @hide */
+    public static final int ATSC3_PLP_INFO = Constants.FrontendScanMessageType.ATSC3_PLP_INFO;
+
     private final int mType;
     private final Object mValue;
 
@@ -33,69 +74,69 @@
     }
 
     /** Gets scan message type. */
-    @ScanMessageType
+    @Type
     public int getMessageType() {
         return mType;
     }
     /** Message indicates whether frontend is locked or not. */
     public boolean getIsLocked() {
-        if (mType != TunerConstants.SCAN_MESSAGE_TYPE_LOCKED) {
+        if (mType != LOCKED) {
             throw new IllegalStateException();
         }
         return (Boolean) mValue;
     }
     /** Message indicates whether the scan has reached the end or not. */
     public boolean getIsEnd() {
-        if (mType != TunerConstants.SCAN_MESSAGE_TYPE_END) {
+        if (mType != END) {
             throw new IllegalStateException();
         }
         return (Boolean) mValue;
     }
     /** Progress message in percent. */
     public int getProgressPercent() {
-        if (mType != TunerConstants.SCAN_MESSAGE_TYPE_PROGRESS_PERCENT) {
+        if (mType != PROGRESS_PERCENT) {
             throw new IllegalStateException();
         }
         return (Integer) mValue;
     }
     /** Gets frequency. */
     public int getFrequency() {
-        if (mType != TunerConstants.SCAN_MESSAGE_TYPE_FREQUENCY) {
+        if (mType != FREQUENCY) {
             throw new IllegalStateException();
         }
         return (Integer) mValue;
     }
     /** Gets symbol rate. */
     public int getSymbolRate() {
-        if (mType != TunerConstants.SCAN_MESSAGE_TYPE_SYMBOL_RATE) {
+        if (mType != SYMBOL_RATE) {
             throw new IllegalStateException();
         }
         return (Integer) mValue;
     }
     /** Gets PLP IDs. */
     public int[] getPlpIds() {
-        if (mType != TunerConstants.SCAN_MESSAGE_TYPE_PLP_IDS) {
+        if (mType != PLP_IDS) {
             throw new IllegalStateException();
         }
         return (int[]) mValue;
     }
     /** Gets group IDs. */
     public int[] getGroupIds() {
-        if (mType != TunerConstants.SCAN_MESSAGE_TYPE_GROUP_IDS) {
+        if (mType != GROUP_IDS) {
             throw new IllegalStateException();
         }
         return (int[]) mValue;
     }
     /** Gets Input stream IDs. */
     public int[] getInputStreamIds() {
-        if (mType != TunerConstants.SCAN_MESSAGE_TYPE_INPUT_STREAM_IDS) {
+        if (mType != INPUT_STREAM_IDS) {
             throw new IllegalStateException();
         }
         return (int[]) mValue;
     }
     /** Gets the DVB-T or DVB-S standard. */
     public int getStandard() {
-        if (mType != TunerConstants.SCAN_MESSAGE_TYPE_STANDARD) {
+        if (mType != STANDARD) {
             throw new IllegalStateException();
         }
         return (int) mValue;
@@ -103,7 +144,7 @@
 
     /** Gets PLP information for ATSC3. */
     public Atsc3PlpInfo[] getAtsc3PlpInfos() {
-        if (mType != TunerConstants.SCAN_MESSAGE_TYPE_ATSC3_PLP_INFO) {
+        if (mType != ATSC3_PLP_INFO) {
             throw new IllegalStateException();
         }
         return (Atsc3PlpInfo[]) mValue;
diff --git a/media/java/android/mtp/MtpPropertyList.java b/media/java/android/mtp/MtpPropertyList.java
index 557f099..53d838d 100644
--- a/media/java/android/mtp/MtpPropertyList.java
+++ b/media/java/android/mtp/MtpPropertyList.java
@@ -16,7 +16,8 @@
 
 package android.mtp;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.util.ArrayList;
 import java.util.List;
 
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
index c7dbca6..ba75263 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -16,9 +16,8 @@
 
 package android.mtp;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.storage.StorageVolume;
-import android.provider.MediaStore;
 
 /**
  * This class represents a storage unit on an MTP device.
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 86a1076..06adf30 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -21,8 +21,8 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.UnsupportedAppUsage;
 import android.app.Service;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index f0f3688..4f1125f 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -696,6 +696,10 @@
     return tuner->openFilter(filterType, bufferSize);
 }
 
+static jobject android_media_tv_Tuner_open_time_filter(JNIEnv, jobject) {
+    return NULL;
+}
+
 static DemuxFilterSettings getFilterSettings(
         JNIEnv *env, int type, int subtype, jobject filterSettingsObj) {
     DemuxFilterSettings filterSettings;
@@ -840,6 +844,28 @@
     return 0;
 }
 
+// TODO: implement TimeFilter functions
+static int android_media_tv_Tuner_time_filter_set_timestamp(
+        JNIEnv, jobject, jlong) {
+    return 0;
+}
+
+static int android_media_tv_Tuner_time_filter_clear_timestamp(JNIEnv, jobject) {
+    return 0;
+}
+
+static jobject android_media_tv_Tuner_time_filter_get_timestamp(JNIEnv, jobject) {
+    return NULL;
+}
+
+static jobject android_media_tv_Tuner_time_filter_get_source_time(JNIEnv, jobject) {
+    return NULL;
+}
+
+static int android_media_tv_Tuner_time_filter_close(JNIEnv, jobject) {
+    return 0;
+}
+
 static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->openDescrambler();
@@ -1119,6 +1145,8 @@
             (void *)android_media_tv_Tuner_get_frontend_info },
     { "nativeOpenFilter", "(III)Landroid/media/tv/tuner/Tuner$Filter;",
             (void *)android_media_tv_Tuner_open_filter },
+    { "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/Tuner$TimeFilter;",
+            (void *)android_media_tv_Tuner_open_time_filter },
     { "nativeGetLnbIds", "()Ljava/util/List;",
             (void *)android_media_tv_Tuner_get_lnb_ids },
     { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Tuner$Lnb;",
@@ -1144,6 +1172,16 @@
     { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter },
 };
 
+static const JNINativeMethod gTimeFilterMethods[] = {
+    { "nativeSetTimeStamp", "(J)I", (void *)android_media_tv_Tuner_time_filter_set_timestamp },
+    { "nativeClearTimeStamp", "()I", (void *)android_media_tv_Tuner_time_filter_clear_timestamp },
+    { "nativeGetTimeStamp", "()Ljava/lang/Long;",
+            (void *)android_media_tv_Tuner_time_filter_get_timestamp },
+    { "nativeGetSourceTime", "()Ljava/lang/Long;",
+            (void *)android_media_tv_Tuner_time_filter_get_source_time },
+    { "nativeClose", "()I", (void *)android_media_tv_Tuner_time_filter_close },
+};
+
 static const JNINativeMethod gDescramblerMethods[] = {
     { "nativeAddPid", "(IILandroid/media/tv/tuner/Tuner$Filter;)I",
             (void *)android_media_tv_Tuner_add_pid },
@@ -1194,6 +1232,13 @@
         return false;
     }
     if (AndroidRuntime::registerNativeMethods(
+            env, "android/media/tv/tuner/Tuner$TimeFilter",
+            gTimeFilterMethods,
+            NELEM(gTimeFilterMethods)) != JNI_OK) {
+        ALOGE("Failed to register time filter native methods");
+        return false;
+    }
+    if (AndroidRuntime::registerNativeMethods(
             env, "android/media/tv/tuner/Tuner$Descrambler",
             gDescramblerMethods,
             NELEM(gDescramblerMethods)) != JNI_OK) {
diff --git a/media/mca/effect/java/android/media/effect/SingleFilterEffect.java b/media/mca/effect/java/android/media/effect/SingleFilterEffect.java
index dfbf5d2..121443f 100644
--- a/media/mca/effect/java/android/media/effect/SingleFilterEffect.java
+++ b/media/mca/effect/java/android/media/effect/SingleFilterEffect.java
@@ -17,12 +17,11 @@
 
 package android.media.effect;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.filterfw.core.Filter;
 import android.filterfw.core.FilterFactory;
 import android.filterfw.core.FilterFunction;
 import android.filterfw.core.Frame;
-import android.media.effect.EffectContext;
 
 /**
  * Effect subclass for effects based on a single Filter. Subclasses need only invoke the
diff --git a/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java b/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java
index 52615bf..3a7f1ed 100644
--- a/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java
+++ b/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java
@@ -17,11 +17,11 @@
 
 package android.filterfw;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.filterfw.core.AsyncRunner;
-import android.filterfw.core.FilterGraph;
 import android.filterfw.core.FilterContext;
+import android.filterfw.core.FilterGraph;
 import android.filterfw.core.FrameManager;
 import android.filterfw.core.GraphRunner;
 import android.filterfw.core.RoundRobinScheduler;
diff --git a/media/mca/filterfw/java/android/filterfw/core/Filter.java b/media/mca/filterfw/java/android/filterfw/core/Filter.java
index 4f56b92..a608ef5 100644
--- a/media/mca/filterfw/java/android/filterfw/core/Filter.java
+++ b/media/mca/filterfw/java/android/filterfw/core/Filter.java
@@ -17,19 +17,15 @@
 
 package android.filterfw.core;
 
-import android.annotation.UnsupportedAppUsage;
-import android.filterfw.core.FilterContext;
-import android.filterfw.core.FilterPort;
-import android.filterfw.core.KeyValueMap;
-import android.filterfw.io.TextGraphReader;
-import android.filterfw.io.GraphIOException;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.filterfw.format.ObjectFormat;
+import android.filterfw.io.GraphIOException;
+import android.filterfw.io.TextGraphReader;
 import android.util.Log;
 
 import java.io.Serializable;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
-import java.lang.Thread;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterContext.java b/media/mca/filterfw/java/android/filterfw/core/FilterContext.java
index a19220e..6b0a219 100644
--- a/media/mca/filterfw/java/android/filterfw/core/FilterContext.java
+++ b/media/mca/filterfw/java/android/filterfw/core/FilterContext.java
@@ -17,11 +17,7 @@
 
 package android.filterfw.core;
 
-import android.annotation.UnsupportedAppUsage;
-import android.filterfw.core.Filter;
-import android.filterfw.core.Frame;
-import android.filterfw.core.FrameManager;
-import android.filterfw.core.GLEnvironment;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import java.util.HashMap;
 import java.util.HashSet;
diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java b/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java
index e6ca11f..35a298f 100644
--- a/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java
+++ b/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java
@@ -17,6 +17,11 @@
 
 package android.filterfw.core;
 
+import android.compat.annotation.UnsupportedAppUsage;
+import android.filterpacks.base.FrameBranch;
+import android.filterpacks.base.NullFilter;
+import android.util.Log;
+
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -25,14 +30,6 @@
 import java.util.Set;
 import java.util.Stack;
 
-import android.filterfw.core.FilterContext;
-import android.filterfw.core.KeyValueMap;
-import android.filterpacks.base.FrameBranch;
-import android.filterpacks.base.NullFilter;
-
-import android.annotation.UnsupportedAppUsage;
-import android.util.Log;
-
 /**
  * @hide
  */
diff --git a/media/mca/filterfw/java/android/filterfw/core/Frame.java b/media/mca/filterfw/java/android/filterfw/core/Frame.java
index e880783..c4d935a 100644
--- a/media/mca/filterfw/java/android/filterfw/core/Frame.java
+++ b/media/mca/filterfw/java/android/filterfw/core/Frame.java
@@ -17,9 +17,7 @@
 
 package android.filterfw.core;
 
-import android.annotation.UnsupportedAppUsage;
-import android.filterfw.core.FrameFormat;
-import android.filterfw.core.FrameManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Bitmap;
 
 import java.nio.ByteBuffer;
diff --git a/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java b/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java
index eb0ff0a..a87e9b9 100644
--- a/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java
+++ b/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java
@@ -17,9 +17,7 @@
 
 package android.filterfw.core;
 
-import android.annotation.UnsupportedAppUsage;
-import android.filterfw.core.KeyValueMap;
-import android.filterfw.core.MutableFrameFormat;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import java.util.Arrays;
 import java.util.Map.Entry;
diff --git a/media/mca/filterfw/java/android/filterfw/core/FrameManager.java b/media/mca/filterfw/java/android/filterfw/core/FrameManager.java
index 85c8fcd..e49aaf1 100644
--- a/media/mca/filterfw/java/android/filterfw/core/FrameManager.java
+++ b/media/mca/filterfw/java/android/filterfw/core/FrameManager.java
@@ -17,10 +17,7 @@
 
 package android.filterfw.core;
 
-import android.annotation.UnsupportedAppUsage;
-import android.filterfw.core.Frame;
-import android.filterfw.core.FrameFormat;
-import android.filterfw.core.MutableFrameFormat;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * @hide
diff --git a/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java b/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java
index e25d6a7..7e4e8a6 100644
--- a/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java
+++ b/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java
@@ -17,13 +17,12 @@
 
 package android.filterfw.core;
 
-import android.annotation.UnsupportedAppUsage;
-import android.filterfw.core.NativeAllocatorTag;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.SurfaceTexture;
+import android.media.MediaRecorder;
 import android.os.Looper;
 import android.util.Log;
 import android.view.Surface;
-import android.media.MediaRecorder;
 
 /**
  * @hide
diff --git a/media/mca/filterfw/java/android/filterfw/core/GLFrame.java b/media/mca/filterfw/java/android/filterfw/core/GLFrame.java
index 9e3025f..1ccd7fe 100644
--- a/media/mca/filterfw/java/android/filterfw/core/GLFrame.java
+++ b/media/mca/filterfw/java/android/filterfw/core/GLFrame.java
@@ -17,15 +17,10 @@
 
 package android.filterfw.core;
 
-import android.annotation.UnsupportedAppUsage;
-import android.filterfw.core.Frame;
-import android.filterfw.core.FrameFormat;
-import android.filterfw.core.FrameManager;
-import android.filterfw.core.NativeFrame;
-import android.filterfw.core.StopWatchMap;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Bitmap;
-import android.opengl.GLES20;
 import android.graphics.Rect;
+import android.opengl.GLES20;
 
 import java.nio.ByteBuffer;
 
diff --git a/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java b/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java
index 250cfaa..b57e8bb 100644
--- a/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java
+++ b/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java
@@ -17,7 +17,7 @@
 
 package android.filterfw.core;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * @hide
diff --git a/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java b/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java
index ae2ad99..da00b1f 100644
--- a/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java
+++ b/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java
@@ -17,9 +17,7 @@
 
 package android.filterfw.core;
 
-import android.annotation.UnsupportedAppUsage;
-import android.filterfw.core.FrameFormat;
-import android.filterfw.core.KeyValueMap;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import java.util.Arrays;
 
diff --git a/media/mca/filterfw/java/android/filterfw/core/Program.java b/media/mca/filterfw/java/android/filterfw/core/Program.java
index 376c085..145388e 100644
--- a/media/mca/filterfw/java/android/filterfw/core/Program.java
+++ b/media/mca/filterfw/java/android/filterfw/core/Program.java
@@ -17,8 +17,7 @@
 
 package android.filterfw.core;
 
-import android.annotation.UnsupportedAppUsage;
-import android.filterfw.core.Frame;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * @hide
diff --git a/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java b/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java
index f41636e..e043be0 100644
--- a/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java
+++ b/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java
@@ -17,12 +17,7 @@
 
 package android.filterfw.core;
 
-import android.annotation.UnsupportedAppUsage;
-import android.filterfw.core.Frame;
-import android.filterfw.core.NativeAllocatorTag;
-import android.filterfw.core.Program;
-import android.filterfw.core.StopWatchMap;
-import android.filterfw.core.VertexFrame;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.filterfw.geometry.Quad;
 import android.opengl.GLES20;
 
diff --git a/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java b/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java
index ac08730..0e05092 100644
--- a/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java
+++ b/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java
@@ -17,7 +17,7 @@
 
 package android.filterfw.format;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.filterfw.core.FrameFormat;
 import android.filterfw.core.MutableFrameFormat;
 import android.graphics.Bitmap;
diff --git a/media/mca/filterfw/java/android/filterfw/geometry/Point.java b/media/mca/filterfw/java/android/filterfw/geometry/Point.java
index d7acf12..96d2d7b 100644
--- a/media/mca/filterfw/java/android/filterfw/geometry/Point.java
+++ b/media/mca/filterfw/java/android/filterfw/geometry/Point.java
@@ -17,8 +17,7 @@
 
 package android.filterfw.geometry;
 
-import android.annotation.UnsupportedAppUsage;
-import java.lang.Math;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * @hide
diff --git a/media/mca/filterfw/java/android/filterfw/geometry/Quad.java b/media/mca/filterfw/java/android/filterfw/geometry/Quad.java
index 610e5b8..2b308a9 100644
--- a/media/mca/filterfw/java/android/filterfw/geometry/Quad.java
+++ b/media/mca/filterfw/java/android/filterfw/geometry/Quad.java
@@ -17,10 +17,8 @@
 
 package android.filterfw.geometry;
 
-import android.annotation.UnsupportedAppUsage;
-import android.filterfw.geometry.Point;
+import android.compat.annotation.UnsupportedAppUsage;
 
-import java.lang.Float;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
index 04fccc7..8c0273b 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -46,8 +46,8 @@
     public static final String ROUTE_ID5_TO_TRANSFER_TO = "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_CATEGORY = "route_special_category";
-    public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
+    public static final String ROUTE_ID_SPECIAL_TYPE = "route_special_type";
+    public static final String ROUTE_NAME_SPECIAL_TYPE = "Special Type Route";
 
     public static final int VOLUME_MAX = 100;
     public static final String ROUTE_ID_FIXED_VOLUME = "route_fixed_volume";
@@ -58,49 +58,49 @@
     public static final String ACTION_REMOVE_ROUTE =
             "com.android.mediarouteprovider.action_remove_route";
 
-    public static final String CATEGORY_SAMPLE =
-            "com.android.mediarouteprovider.CATEGORY_SAMPLE";
-    public static final String CATEGORY_SPECIAL =
-            "com.android.mediarouteprovider.CATEGORY_SPECIAL";
+    public static final String TYPE_SAMPLE =
+            "com.android.mediarouteprovider.TYPE_SAMPLE";
+    public static final String TYPE_SPECIAL =
+            "com.android.mediarouteprovider.TYPE_SPECIAL";
 
     Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
-    Map<String, Integer> mRouteSessionMap = new HashMap<>();
+    Map<String, String> mRouteSessionMap = new HashMap<>();
     private int mNextSessionId = 1000;
 
     private void initializeRoutes() {
         MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1)
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .setDeviceType(DEVICE_TYPE_TV)
                 .build();
         MediaRoute2Info route2 = new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2)
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .setDeviceType(DEVICE_TYPE_SPEAKER)
                 .build();
         MediaRoute2Info route3 = new MediaRoute2Info.Builder(
                 ROUTE_ID3_SESSION_CREATION_FAILED, ROUTE_NAME3)
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .build();
         MediaRoute2Info route4 = new MediaRoute2Info.Builder(
                 ROUTE_ID4_TO_SELECT_AND_DESELECT, ROUTE_NAME4)
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .build();
         MediaRoute2Info route5 = new MediaRoute2Info.Builder(
                 ROUTE_ID5_TO_TRANSFER_TO, ROUTE_NAME5)
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .build();
         MediaRoute2Info routeSpecial =
-                new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_CATEGORY, ROUTE_NAME_SPECIAL_CATEGORY)
-                        .addSupportedCategory(CATEGORY_SAMPLE)
-                        .addSupportedCategory(CATEGORY_SPECIAL)
+                new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_TYPE, ROUTE_NAME_SPECIAL_TYPE)
+                        .addRouteType(TYPE_SAMPLE)
+                        .addRouteType(TYPE_SPECIAL)
                         .build();
         MediaRoute2Info fixedVolumeRoute =
                 new MediaRoute2Info.Builder(ROUTE_ID_FIXED_VOLUME, ROUTE_NAME_FIXED_VOLUME)
-                        .addSupportedCategory(CATEGORY_SAMPLE)
+                        .addRouteType(TYPE_SAMPLE)
                         .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_FIXED)
                         .build();
         MediaRoute2Info variableVolumeRoute =
                 new MediaRoute2Info.Builder(ROUTE_ID_VARIABLE_VOLUME, ROUTE_NAME_VARIABLE_VOLUME)
-                        .addSupportedCategory(CATEGORY_SAMPLE)
+                        .addRouteType(TYPE_SAMPLE)
                         .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
                         .setVolumeMax(VOLUME_MAX)
                         .build();
@@ -167,7 +167,7 @@
     }
 
     @Override
-    public void onCreateSession(String packageName, String routeId, String controlCategory,
+    public void onCreateSession(String packageName, String routeId, String routeType,
             long requestId) {
         MediaRoute2Info route = mRoutes.get(routeId);
         if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) {
@@ -177,7 +177,7 @@
         }
         maybeDeselectRoute(routeId);
 
-        final int sessionId = mNextSessionId;
+        final String sessionId = String.valueOf(mNextSessionId);
         mNextSessionId++;
 
         mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
@@ -186,7 +186,7 @@
         mRouteSessionMap.put(routeId, sessionId);
 
         RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
-                sessionId, packageName, controlCategory)
+                sessionId, packageName, routeType)
                 .addSelectedRoute(routeId)
                 .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                 .addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO)
@@ -196,7 +196,7 @@
     }
 
     @Override
-    public void onDestroySession(int sessionId, RouteSessionInfo lastSessionInfo) {
+    public void onDestroySession(String sessionId, RouteSessionInfo lastSessionInfo) {
         for (String routeId : lastSessionInfo.getSelectedRoutes()) {
             mRouteSessionMap.remove(routeId);
             MediaRoute2Info route = mRoutes.get(routeId);
@@ -209,7 +209,7 @@
     }
 
     @Override
-    public void onSelectRoute(int sessionId, String routeId) {
+    public void onSelectRoute(String sessionId, String routeId) {
         RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
         MediaRoute2Info route = mRoutes.get(routeId);
         if (route == null || sessionInfo == null) {
@@ -218,7 +218,7 @@
         maybeDeselectRoute(routeId);
 
         mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
-                .setClientPackageName(sessionInfo.getPackageName())
+                .setClientPackageName(sessionInfo.getClientPackageName())
                 .build());
         mRouteSessionMap.put(routeId, sessionId);
 
@@ -232,7 +232,7 @@
     }
 
     @Override
-    public void onDeselectRoute(int sessionId, String routeId) {
+    public void onDeselectRoute(String sessionId, String routeId) {
         RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
         MediaRoute2Info route = mRoutes.get(routeId);
 
@@ -254,7 +254,7 @@
     }
 
     @Override
-    public void onTransferToRoute(int sessionId, String routeId) {
+    public void onTransferToRoute(String sessionId, String routeId) {
         RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
         RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
                 .clearSelectedRoutes()
@@ -271,7 +271,7 @@
             return;
         }
 
-        int sessionId = mRouteSessionMap.get(routeId);
+        String sessionId = mRouteSessionMap.get(routeId);
         onDeselectRoute(sessionId, routeId);
     }
 
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index 6fe847b..ce4bb8e 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -23,18 +23,18 @@
 import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
 import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
 
-import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORIES_ALL;
-import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORIES_SPECIAL;
-import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORY_SAMPLE;
-import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORY_SPECIAL;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID1;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID2;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID3_SESSION_CREATION_FAILED;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID4_TO_SELECT_AND_DESELECT;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID5_TO_TRANSFER_TO;
-import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_SPECIAL_CATEGORY;
+import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_SPECIAL_TYPE;
 import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_VARIABLE_VOLUME;
 import static com.android.mediaroutertest.MediaRouterManagerTest.SYSTEM_PROVIDER_ID;
+import static com.android.mediaroutertest.MediaRouterManagerTest.TYPES_ALL;
+import static com.android.mediaroutertest.MediaRouterManagerTest.TYPES_SPECIAL;
+import static com.android.mediaroutertest.MediaRouterManagerTest.TYPE_SAMPLE;
+import static com.android.mediaroutertest.MediaRouterManagerTest.TYPE_SPECIAL;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -50,6 +50,7 @@
 import android.media.MediaRouter2.RouteCallback;
 import android.media.MediaRouter2.RouteSessionController;
 import android.media.MediaRouter2.SessionCallback;
+import android.media.RouteDiscoveryRequest;
 import android.media.RouteSessionInfo;
 import android.net.Uri;
 import android.os.Parcel;
@@ -95,14 +96,14 @@
     }
 
     /**
-     * Tests if we get proper routes for application that has special control category.
+     * Tests if we get proper routes for application that has special route type.
      */
     @Test
     public void testGetRoutes() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_SPECIAL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(TYPES_SPECIAL);
 
         assertEquals(1, routes.size());
-        assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
+        assertNotNull(routes.get(ROUTE_ID_SPECIAL_TYPE));
     }
 
     @Test
@@ -114,7 +115,7 @@
                 .setIconUri(new Uri.Builder().path("icon").build())
                 .setVolume(5)
                 .setVolumeMax(20)
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
                 .setDeviceType(DEVICE_TYPE_SPEAKER)
                 .build();
@@ -137,7 +138,7 @@
                 .setClientPackageName("com.android.mediaroutertest")
                 .setConnectionState(CONNECTION_STATE_CONNECTING)
                 .setIconUri(new Uri.Builder().path("icon").build())
-                .addSupportedCategory(CATEGORY_SAMPLE)
+                .addRouteType(TYPE_SAMPLE)
                 .setVolume(5)
                 .setVolumeMax(20)
                 .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
@@ -168,9 +169,9 @@
                 .setClientPackageName("another.client.package").build();
         assertNotEquals(route, routeClient);
 
-        MediaRoute2Info routeCategory = new MediaRoute2Info.Builder(route)
-                .addSupportedCategory(CATEGORY_SPECIAL).build();
-        assertNotEquals(route, routeCategory);
+        MediaRoute2Info routeType = new MediaRoute2Info.Builder(route)
+                .addRouteType(TYPE_SPECIAL).build();
+        assertNotEquals(route, routeType);
 
         MediaRoute2Info routeVolume = new MediaRoute2Info.Builder(route)
                 .setVolume(10).build();
@@ -191,7 +192,7 @@
 
     @Test
     public void testControlVolumeWithRouter() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(TYPES_ALL);
 
         MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
         assertNotNull(volRoute);
@@ -202,12 +203,14 @@
         awaitOnRouteChanged(
                 () -> mRouter2.requestUpdateVolume(volRoute, deltaVolume),
                 ROUTE_ID_VARIABLE_VOLUME,
-                (route -> route.getVolume() == originalVolume + deltaVolume));
+                (route -> route.getVolume() == originalVolume + deltaVolume),
+                TYPES_ALL);
 
         awaitOnRouteChanged(
                 () -> mRouter2.requestSetVolume(volRoute, originalVolume),
                 ROUTE_ID_VARIABLE_VOLUME,
-                (route -> route.getVolume() == originalVolume));
+                (route -> route.getVolume() == originalVolume),
+                TYPES_ALL);
     }
 
     @Test
@@ -234,13 +237,13 @@
     @Test
     public void testRequestCreateSessionWithInvalidArguments() {
         MediaRoute2Info route = new MediaRoute2Info.Builder("id", "name").build();
-        String controlCategory = "controlCategory";
+        String routeType = "routeType";
 
         // Tests null route
         assertThrows(NullPointerException.class,
-                () -> mRouter2.requestCreateSession(null, controlCategory));
+                () -> mRouter2.requestCreateSession(null, routeType));
 
-        // Tests null or empty control category
+        // Tests null or empty route type
         assertThrows(IllegalArgumentException.class,
                 () -> mRouter2.requestCreateSession(route, null));
         assertThrows(IllegalArgumentException.class,
@@ -249,10 +252,10 @@
 
     @Test
     public void testRequestCreateSessionSuccess() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info route = routes.get(ROUTE_ID1);
         assertNotNull(route);
 
@@ -266,25 +269,25 @@
             public void onSessionCreated(RouteSessionController controller) {
                 assertNotNull(controller);
                 assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1));
-                assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory()));
+                assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType()));
                 controllers.add(controller);
                 successLatch.countDown();
             }
 
             @Override
             public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedControlCategory) {
+                    String requestedRouteType) {
                 failureLatch.countDown();
             }
         };
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(route, TYPE_SAMPLE);
             assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // onSessionCreationFailed should not be called.
@@ -298,10 +301,10 @@
 
     @Test
     public void testRequestCreateSessionFailure() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info route = routes.get(ROUTE_ID3_SESSION_CREATION_FAILED);
         assertNotNull(route);
 
@@ -319,20 +322,20 @@
 
             @Override
             public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedControlCategory) {
+                    String requestedRouteType) {
                 assertEquals(route, requestedRoute);
-                assertTrue(TextUtils.equals(CATEGORY_SAMPLE, requestedControlCategory));
+                assertTrue(TextUtils.equals(TYPE_SAMPLE, requestedRouteType));
                 failureLatch.countDown();
             }
         };
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(route, TYPE_SAMPLE);
             assertTrue(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // onSessionCreated should not be called.
@@ -346,8 +349,8 @@
 
     @Test
     public void testRequestCreateSessionMultipleSessions() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
         final CountDownLatch successLatch = new CountDownLatch(2);
         final CountDownLatch failureLatch = new CountDownLatch(1);
@@ -363,12 +366,12 @@
 
             @Override
             public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedControlCategory) {
+                    String requestedRouteType) {
                 failureLatch.countDown();
             }
         };
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info route1 = routes.get(ROUTE_ID1);
         MediaRoute2Info route2 = routes.get(ROUTE_ID2);
         assertNotNull(route1);
@@ -376,12 +379,12 @@
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route1, CATEGORY_SAMPLE);
-            mRouter2.requestCreateSession(route2, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(route1, TYPE_SAMPLE);
+            mRouter2.requestCreateSession(route2, TYPE_SAMPLE);
             assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // onSessionCreationFailed should not be called.
@@ -395,8 +398,9 @@
             assertNotEquals(controller1.getSessionId(), controller2.getSessionId());
             assertTrue(createRouteMap(controller1.getSelectedRoutes()).containsKey(ROUTE_ID1));
             assertTrue(createRouteMap(controller2.getSelectedRoutes()).containsKey(ROUTE_ID2));
-            assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller1.getControlCategory()));
-            assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller2.getControlCategory()));
+            assertTrue(TextUtils.equals(TYPE_SAMPLE, controller1.getRouteType()));
+            assertTrue(TextUtils.equals(TYPE_SAMPLE, controller2.getRouteType()));
+
         } finally {
             releaseControllers(createdControllers);
             mRouter2.unregisterRouteCallback(routeCallback);
@@ -406,10 +410,10 @@
 
     @Test
     public void testSessionCallbackIsNotCalledAfterUnregistered() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info route = routes.get(ROUTE_ID1);
         assertNotNull(route);
 
@@ -427,18 +431,18 @@
 
             @Override
             public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedControlCategory) {
+                    String requestedRouteType) {
                 failureLatch.countDown();
             }
         };
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(route, TYPE_SAMPLE);
 
             // Unregisters session callback
             mRouter2.unregisterSessionCallback(sessionCallback);
@@ -456,10 +460,10 @@
     // TODO: Add tests for illegal inputs if needed (e.g. selecting already selected route)
     @Test
     public void testRouteSessionControllerSelectAndDeselectRoute() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
         assertNotNull(routeToCreateSessionWith);
 
@@ -474,7 +478,7 @@
             public void onSessionCreated(RouteSessionController controller) {
                 assertNotNull(controller);
                 assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
-                assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory()));
+                assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType()));
                 controllers.add(controller);
                 onSessionCreatedLatch.countDown();
             }
@@ -483,20 +487,21 @@
             public void onSessionInfoChanged(RouteSessionController controller,
                     RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
                 if (onSessionCreatedLatch.getCount() != 0
-                        || controllers.get(0).getSessionId() != controller.getSessionId()) {
+                        || !TextUtils.equals(
+                                controllers.get(0).getSessionId(), controller.getSessionId())) {
                     return;
                 }
 
                 if (onSessionInfoChangedLatchForSelect.getCount() != 0) {
                     // Check oldInfo
-                    assertEquals(controller.getSessionId(), oldInfo.getSessionId());
+                    assertEquals(controller.getSessionId(), oldInfo.getId());
                     assertEquals(1, oldInfo.getSelectedRoutes().size());
                     assertTrue(oldInfo.getSelectedRoutes().contains(ROUTE_ID1));
                     assertTrue(oldInfo.getSelectableRoutes().contains(
                             ROUTE_ID4_TO_SELECT_AND_DESELECT));
 
                     // Check newInfo
-                    assertEquals(controller.getSessionId(), newInfo.getSessionId());
+                    assertEquals(controller.getSessionId(), newInfo.getId());
                     assertEquals(2, newInfo.getSelectedRoutes().size());
                     assertTrue(newInfo.getSelectedRoutes().contains(ROUTE_ID1));
                     assertTrue(newInfo.getSelectedRoutes().contains(
@@ -507,7 +512,7 @@
                     onSessionInfoChangedLatchForSelect.countDown();
                 } else {
                     // Check newInfo
-                    assertEquals(controller.getSessionId(), newInfo.getSessionId());
+                    assertEquals(controller.getSessionId(), newInfo.getId());
                     assertEquals(1, newInfo.getSelectedRoutes().size());
                     assertTrue(newInfo.getSelectedRoutes().contains(ROUTE_ID1));
                     assertFalse(newInfo.getSelectedRoutes().contains(
@@ -522,11 +527,11 @@
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(routeToCreateSessionWith, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(routeToCreateSessionWith, TYPE_SAMPLE);
             assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             assertEquals(1, controllers.size());
@@ -554,10 +559,10 @@
 
     @Test
     public void testRouteSessionControllerTransferToRoute() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
         assertNotNull(routeToCreateSessionWith);
 
@@ -570,8 +575,12 @@
             @Override
             public void onSessionCreated(RouteSessionController controller) {
                 assertNotNull(controller);
+                android.util.Log.d(TAG, "selected route ids ");
+                for (String routeId : getRouteIds(controller.getSelectedRoutes())) {
+                    android.util.Log.d(TAG, "route id : " + routeId);
+                }
                 assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
-                assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory()));
+                assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType()));
                 controllers.add(controller);
                 onSessionCreatedLatch.countDown();
             }
@@ -580,18 +589,19 @@
             public void onSessionInfoChanged(RouteSessionController controller,
                     RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
                 if (onSessionCreatedLatch.getCount() != 0
-                        || controllers.get(0).getSessionId() != controller.getSessionId()) {
+                        || !TextUtils.equals(
+                                controllers.get(0).getSessionId(), controller.getSessionId())) {
                     return;
                 }
 
                 // Check oldInfo
-                assertEquals(controller.getSessionId(), oldInfo.getSessionId());
+                assertEquals(controller.getSessionId(), oldInfo.getId());
                 assertEquals(1, oldInfo.getSelectedRoutes().size());
                 assertTrue(oldInfo.getSelectedRoutes().contains(ROUTE_ID1));
                 assertTrue(oldInfo.getTransferrableRoutes().contains(ROUTE_ID5_TO_TRANSFER_TO));
 
                 // Check newInfo
-                assertEquals(controller.getSessionId(), newInfo.getSessionId());
+                assertEquals(controller.getSessionId(), newInfo.getId());
                 assertEquals(1, newInfo.getSelectedRoutes().size());
                 assertFalse(newInfo.getSelectedRoutes().contains(ROUTE_ID1));
                 assertTrue(newInfo.getSelectedRoutes().contains(ROUTE_ID5_TO_TRANSFER_TO));
@@ -603,11 +613,11 @@
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(routeToCreateSessionWith, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(routeToCreateSessionWith, TYPE_SAMPLE);
             assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             assertEquals(1, controllers.size());
@@ -632,10 +642,10 @@
 
     @Test
     public void testRouteSessionControllerReleaseShouldIgnoreTransferTo() throws Exception {
-        final List<String> sampleControlCategory = new ArrayList<>();
-        sampleControlCategory.add(CATEGORY_SAMPLE);
+        final List<String> sampleRouteType = new ArrayList<>();
+        sampleRouteType.add(TYPE_SAMPLE);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
         assertNotNull(routeToCreateSessionWith);
 
@@ -649,7 +659,7 @@
             public void onSessionCreated(RouteSessionController controller) {
                 assertNotNull(controller);
                 assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
-                assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory()));
+                assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType()));
                 controllers.add(controller);
                 onSessionCreatedLatch.countDown();
             }
@@ -658,7 +668,8 @@
             public void onSessionInfoChanged(RouteSessionController controller,
                     RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
                 if (onSessionCreatedLatch.getCount() != 0
-                        || controllers.get(0).getSessionId() != controller.getSessionId()) {
+                        || !TextUtils.equals(
+                                controllers.get(0).getSessionId(), controller.getSessionId())) {
                     return;
                 }
                 onSessionInfoChangedLatch.countDown();
@@ -667,11 +678,11 @@
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(routeToCreateSessionWith, CATEGORY_SAMPLE);
+            mRouter2.requestCreateSession(routeToCreateSessionWith, TYPE_SAMPLE);
             assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             assertEquals(1, controllers.size());
@@ -701,17 +712,16 @@
     static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
         Map<String, MediaRoute2Info> routeMap = new HashMap<>();
         for (MediaRoute2Info route : routes) {
-            // intentionally not using route.getUniqueId() for convenience.
             routeMap.put(route.getId(), route);
         }
         return routeMap;
     }
 
-    Map<String, MediaRoute2Info> waitAndGetRoutes(List<String> controlCategories)
+    Map<String, MediaRoute2Info> waitAndGetRoutes(List<String> routeTypes)
             throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
 
-        // A dummy callback is required to send control category info.
+        // A dummy callback is required to send route type info.
         RouteCallback routeCallback = new RouteCallback() {
             @Override
             public void onRoutesAdded(List<MediaRoute2Info> routes) {
@@ -724,8 +734,8 @@
             }
         };
 
-        mRouter2.setControlCategories(controlCategories);
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback,
+                new RouteDiscoveryRequest.Builder(routeTypes, true).build());
         try {
             latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
             return createRouteMap(mRouter2.getRoutes());
@@ -741,7 +751,7 @@
     }
 
     /**
-     * Returns a list of IDs (not uniqueId) of the given route list.
+     * Returns a list of IDs of the given route list.
      */
     List<String> getRouteIds(@NonNull List<MediaRoute2Info> routes) {
         List<String> result = new ArrayList<>();
@@ -752,7 +762,8 @@
     }
 
     void awaitOnRouteChanged(Runnable task, String routeId,
-            Predicate<MediaRoute2Info> predicate) throws Exception {
+            Predicate<MediaRoute2Info> predicate,
+            List<String> routeTypes) throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
         RouteCallback routeCallback = new RouteCallback() {
             @Override
@@ -763,7 +774,8 @@
                 }
             }
         };
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback,
+                new RouteDiscoveryRequest.Builder(routeTypes, true).build());
         try {
             task.run();
             assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 83c7c17..9ff9177c 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -31,6 +31,7 @@
 import android.media.MediaRouter2.RouteCallback;
 import android.media.MediaRouter2.SessionCallback;
 import android.media.MediaRouter2Manager;
+import android.media.RouteDiscoveryRequest;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -57,41 +58,48 @@
 public class MediaRouterManagerTest {
     private static final String TAG = "MediaRouterManagerTest";
 
-    // Must be the same as SampleMediaRoute2ProviderService
-    public static final String ROUTE_ID1 = "route_id1";
+    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 = "route_id2";
+    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 =
-            "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 =
-            "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 = "route_id5_to_transfer_to";
+    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_CATEGORY = "route_special_category";
-    public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
+    public static final String ROUTE_ID_SPECIAL_TYPE =
+            SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_special_type";
+    public static final String ROUTE_NAME_SPECIAL_TYPE = "Special Type 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 = "route_fixed_volume";
+    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 = "route_variable_volume";
+    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 CATEGORY_SAMPLE =
-            "com.android.mediarouteprovider.CATEGORY_SAMPLE";
-    public static final String CATEGORY_SPECIAL =
-            "com.android.mediarouteprovider.CATEGORY_SPECIAL";
+    public static final String TYPE_SAMPLE =
+            "com.android.mediarouteprovider.TYPE_SAMPLE";
+    public static final String TYPE_SPECIAL =
+            "com.android.mediarouteprovider.TYPE_SPECIAL";
 
-    private static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
+    private static final String TYPE_LIVE_AUDIO = "android.media.intent.route.TYPE_LIVE_AUDIO";
 
     private static final int TIMEOUT_MS = 5000;
 
@@ -105,18 +113,18 @@
     private final List<RouteCallback> mRouteCallbacks = new ArrayList<>();
     private final List<SessionCallback> mSessionCallbacks = new ArrayList<>();
 
-    public static final List<String> CATEGORIES_ALL = new ArrayList();
-    public static final List<String> CATEGORIES_SPECIAL = new ArrayList();
-    private static final List<String> CATEGORIES_LIVE_AUDIO = new ArrayList<>();
+    public static final List<String> TYPES_ALL = new ArrayList();
+    public static final List<String> TYPES_SPECIAL = new ArrayList();
+    private static final List<String> TYPES_LIVE_AUDIO = new ArrayList<>();
 
     static {
-        CATEGORIES_ALL.add(CATEGORY_SAMPLE);
-        CATEGORIES_ALL.add(CATEGORY_SPECIAL);
-        CATEGORIES_ALL.add(CATEGORY_LIVE_AUDIO);
+        TYPES_ALL.add(TYPE_SAMPLE);
+        TYPES_ALL.add(TYPE_SPECIAL);
+        TYPES_ALL.add(TYPE_LIVE_AUDIO);
 
-        CATEGORIES_SPECIAL.add(CATEGORY_SPECIAL);
+        TYPES_SPECIAL.add(TYPE_SPECIAL);
 
-        CATEGORIES_LIVE_AUDIO.add(CATEGORY_LIVE_AUDIO);
+        TYPES_LIVE_AUDIO.add(TYPE_LIVE_AUDIO);
     }
 
     @Before
@@ -173,7 +181,7 @@
     @Test
     public void testOnRoutesRemoved() throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
 
         addRouterCallback(new RouteCallback());
         addManagerCallback(new MediaRouter2Manager.Callback() {
@@ -195,14 +203,14 @@
     }
 
     /**
-     * Tests if we get proper routes for application that has special control category.
+     * Tests if we get proper routes for application that has special route type.
      */
     @Test
-    public void testControlCategory() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_SPECIAL);
+    public void testRouteType() throws Exception {
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_SPECIAL);
 
         assertEquals(1, routes.size());
-        assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
+        assertNotNull(routes.get(ROUTE_ID_SPECIAL_TYPE));
     }
 
     /**
@@ -211,7 +219,7 @@
      */
     @Test
     public void testRouterOnSessionCreated() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
 
         CountDownLatch latch = new CountDownLatch(1);
 
@@ -247,7 +255,7 @@
     @Ignore("TODO: test session created callback instead of onRouteSelected")
     public void testManagerOnRouteSelected() throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
 
         addRouterCallback(new RouteCallback());
         addManagerCallback(new MediaRouter2Manager.Callback() {
@@ -277,7 +285,7 @@
     public void testGetActiveRoutes() throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
         addRouterCallback(new RouteCallback());
         addManagerCallback(new MediaRouter2Manager.Callback() {
             @Override
@@ -313,7 +321,7 @@
     @Test
     @Ignore("TODO: enable when session is released")
     public void testSingleProviderSelect() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
         addRouterCallback(new RouteCallback());
 
         awaitOnRouteChangedManager(
@@ -338,7 +346,7 @@
 
     @Test
     public void testControlVolumeWithManager() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
         MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
 
         int originalVolume = volRoute.getVolume();
@@ -357,7 +365,7 @@
 
     @Test
     public void testVolumeHandling() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
 
         MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME);
         MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
@@ -367,11 +375,11 @@
         assertEquals(VOLUME_MAX, variableVolumeRoute.getVolumeMax());
     }
 
-    Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> controlCategories)
+    Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> routeTypes)
             throws Exception {
         CountDownLatch latch = new CountDownLatch(2);
 
-        // A dummy callback is required to send control category info.
+        // A dummy callback is required to send route type info.
         RouteCallback routeCallback = new RouteCallback();
         MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
             @Override
@@ -386,16 +394,16 @@
             }
 
             @Override
-            public void onControlCategoriesChanged(String packageName, List<String> categories) {
+            public void onControlCategoriesChanged(String packageName, List<String> routeTypes) {
                 if (TextUtils.equals(mPackageName, packageName)
-                        && controlCategories.equals(categories)) {
+                        && routeTypes.equals(routeTypes)) {
                     latch.countDown();
                 }
             }
         };
         mManager.registerCallback(mExecutor, managerCallback);
-        mRouter2.setControlCategories(controlCategories);
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback,
+                new RouteDiscoveryRequest.Builder(routeTypes, true).build());
         try {
             latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
             return createRouteMap(mManager.getAvailableRoutes(mPackageName));
@@ -430,7 +438,6 @@
     static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
         Map<String, MediaRoute2Info> routeMap = new HashMap<>();
         for (MediaRoute2Info route : routes) {
-            // intentionally not using route.getUniqueId() for convenience.
             routeMap.put(route.getId(), route);
         }
         return routeMap;
@@ -443,7 +450,7 @@
 
     private void addRouterCallback(RouteCallback routeCallback) {
         mRouteCallbacks.add(routeCallback);
-        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
     }
 
     private void addSessionCallback(SessionCallback sessionCallback) {
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteDiscoveryRequestTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteDiscoveryRequestTest.java
new file mode 100644
index 0000000..60d131a
--- /dev/null
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteDiscoveryRequestTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.mediaroutertest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.media.RouteDiscoveryRequest;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RouteDiscoveryRequestTest {
+    @Before
+    public void setUp() throws Exception { }
+
+    @After
+    public void tearDown() throws Exception { }
+
+    @Test
+    public void testEquality() {
+        List<String> testTypes = new ArrayList<>();
+        testTypes.add("TEST_TYPE_1");
+        testTypes.add("TEST_TYPE_2");
+        RouteDiscoveryRequest request = new RouteDiscoveryRequest.Builder(testTypes, true)
+                .build();
+
+        RouteDiscoveryRequest requestRebuilt = new RouteDiscoveryRequest.Builder(request)
+                .build();
+
+        assertEquals(request, requestRebuilt);
+
+        Parcel parcel = Parcel.obtain();
+        parcel.writeParcelable(request, 0);
+        parcel.setDataPosition(0);
+        RouteDiscoveryRequest requestFromParcel = parcel.readParcelable(null);
+
+        assertEquals(request, requestFromParcel);
+    }
+
+    @Test
+    public void testInequality() {
+        List<String> testTypes = new ArrayList<>();
+        testTypes.add("TEST_TYPE_1");
+        testTypes.add("TEST_TYPE_2");
+
+        List<String> testTypes2 = new ArrayList<>();
+        testTypes.add("TEST_TYPE_3");
+
+        RouteDiscoveryRequest request = new RouteDiscoveryRequest.Builder(testTypes, true)
+                .build();
+
+        RouteDiscoveryRequest requestTypes = new RouteDiscoveryRequest.Builder(request)
+                .setRouteTypes(testTypes2)
+                .build();
+        assertNotEquals(request, requestTypes);
+
+        RouteDiscoveryRequest requestActiveScan = new RouteDiscoveryRequest.Builder(request)
+                .setActiveScan(false)
+                .build();
+        assertNotEquals(request, requestActiveScan);
+    }
+}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java
index 2e81a64..9971fc3 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java
@@ -29,6 +29,7 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class RouteSessionTest {
+    private static final String TEST_SESSION_ID = "test_session_id";
     private static final String TEST_PACKAGE_NAME = "com.android.mediaroutertest";
     private static final String TEST_CONTROL_CATEGORY = "com.android.mediaroutertest.category";
 
@@ -36,17 +37,17 @@
 
     @Test
     public void testValidity() {
-        RouteSessionInfo emptyPackageSession = new RouteSessionInfo.Builder(1,
+        RouteSessionInfo emptyPackageSession = new RouteSessionInfo.Builder(TEST_SESSION_ID,
                 "",
                 TEST_CONTROL_CATEGORY)
                 .addSelectedRoute(TEST_ROUTE_ID1)
                 .build();
-        RouteSessionInfo emptyCategorySession = new RouteSessionInfo.Builder(1,
+        RouteSessionInfo emptyCategorySession = new RouteSessionInfo.Builder(TEST_SESSION_ID,
                 TEST_PACKAGE_NAME, "")
                 .addSelectedRoute(TEST_ROUTE_ID1)
                 .build();
 
-        RouteSessionInfo emptySelectedRouteSession = new RouteSessionInfo.Builder(1,
+        RouteSessionInfo emptySelectedRouteSession = new RouteSessionInfo.Builder(TEST_SESSION_ID,
                 TEST_PACKAGE_NAME, TEST_CONTROL_CATEGORY)
                 .build();
 
@@ -54,9 +55,9 @@
                 .addSelectedRoute(TEST_ROUTE_ID1)
                 .build();
 
-        assertFalse(emptySelectedRouteSession.isValid());
         assertFalse(emptyPackageSession.isValid());
         assertFalse(emptyCategorySession.isValid());
+        assertFalse(emptySelectedRouteSession.isValid());
         assertTrue(validSession.isValid());
     }
 }
diff --git a/mms/OWNERS b/mms/OWNERS
index ba00d5d..befc320 100644
--- a/mms/OWNERS
+++ b/mms/OWNERS
@@ -12,3 +12,5 @@
 shuoq@google.com
 refuhoo@google.com
 nazaninb@google.com
+sarahchin@google.com
+dbright@google.com
\ No newline at end of file
diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java
index 6554267..24ea3cf 100644
--- a/mms/java/android/telephony/MmsManager.java
+++ b/mms/java/android/telephony/MmsManager.java
@@ -16,8 +16,11 @@
 
 package android.telephony;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
+import android.content.Context;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -27,22 +30,16 @@
 
 /**
  * Manages MMS operations such as sending multimedia messages.
- * Get this object by calling the static method {@link #getInstance()}.
- * @hide
  */
-public class MmsManager {
+public final class MmsManager {
     private static final String TAG = "MmsManager";
-
-    /** Singleton object constructed during class initialization. */
-    private static final MmsManager sInstance = new MmsManager();
+    private final Context mContext;
 
     /**
-     * Get the MmsManager singleton instance.
-     *
-     * @return the {@link MmsManager} singleton instance.
+     * @hide
      */
-    public static MmsManager getInstance() {
-        return sInstance;
+    public MmsManager(@NonNull Context context) {
+        mContext = context;
     }
 
     /**
@@ -56,8 +53,9 @@
      * @param sentIntent if not NULL this <code>PendingIntent</code> is broadcast when the message
      *                   is successfully sent, or failed
      */
-    public void sendMultimediaMessage(int subId, Uri contentUri, String locationUrl,
-            Bundle configOverrides, PendingIntent sentIntent) {
+    public void sendMultimediaMessage(int subId, @NonNull Uri contentUri,
+            @Nullable String locationUrl, @Nullable Bundle configOverrides,
+            @Nullable PendingIntent sentIntent) {
         try {
             final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
             if (iMms == null) {
@@ -84,8 +82,9 @@
      *  broadcast when the message is downloaded, or the download is failed
      * @throws IllegalArgumentException if locationUrl or contentUri is empty
      */
-    public void downloadMultimediaMessage(int subId, String locationUrl, Uri contentUri,
-            Bundle configOverrides, PendingIntent downloadedIntent) {
+    public void downloadMultimediaMessage(int subId, @NonNull String locationUrl,
+            @NonNull Uri contentUri, @Nullable Bundle configOverrides,
+            @Nullable PendingIntent downloadedIntent) {
         try {
             final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
             if (iMms == null) {
diff --git a/opengl/java/android/opengl/EGL14.java b/opengl/java/android/opengl/EGL14.java
index 728e6e1..90b46fd 100644
--- a/opengl/java/android/opengl/EGL14.java
+++ b/opengl/java/android/opengl/EGL14.java
@@ -18,11 +18,11 @@
 
 package android.opengl;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.SurfaceTexture;
 import android.view.Surface;
-import android.view.SurfaceView;
 import android.view.SurfaceHolder;
+import android.view.SurfaceView;
 
 /**
  * EGL 1.4
diff --git a/opengl/java/android/opengl/GLES20.java b/opengl/java/android/opengl/GLES20.java
index d66e7ac..e853e44 100644
--- a/opengl/java/android/opengl/GLES20.java
+++ b/opengl/java/android/opengl/GLES20.java
@@ -19,7 +19,7 @@
 
 package android.opengl;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /** OpenGL ES 2.0
  */
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 8a3e6a0..75131b0 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -16,7 +16,7 @@
 
 package android.opengl;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Trace;
 import android.util.AttributeSet;
diff --git a/opengl/java/javax/microedition/khronos/egl/EGL10.java b/opengl/java/javax/microedition/khronos/egl/EGL10.java
index 8a25170..ea571c7 100644
--- a/opengl/java/javax/microedition/khronos/egl/EGL10.java
+++ b/opengl/java/javax/microedition/khronos/egl/EGL10.java
@@ -16,8 +16,7 @@
 
 package javax.microedition.khronos.egl;
 
-import android.annotation.UnsupportedAppUsage;
-import java.lang.String;
+import android.compat.annotation.UnsupportedAppUsage;
 
 public interface EGL10 extends EGL {
     int EGL_SUCCESS                     = 0x3000;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index ed945e7..f7802d2 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -18,6 +18,7 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.logging.NotifLog;
@@ -40,8 +41,9 @@
             NotifLog notifLog,
             NotificationGroupManager groupManager,
             NotificationRankingManager rankingManager,
-            KeyguardEnvironment keyguardEnvironment) {
-        super(notifLog, groupManager, rankingManager, keyguardEnvironment);
+            KeyguardEnvironment keyguardEnvironment,
+            FeatureFlags featureFlags) {
+        super(notifLog, groupManager, rankingManager, keyguardEnvironment, featureFlags);
     }
 
     @Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 3c3ebe2..1ee8518 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -110,6 +110,7 @@
 import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
@@ -276,6 +277,7 @@
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
             NotificationEntryManager notificationEntryManager,
+            NotificationRowContentBinder notificationRowContentBinder,
             NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             NotificationViewHierarchyManager notificationViewHierarchyManager,
             KeyguardViewMediator keyguardViewMediator,
@@ -363,6 +365,7 @@
                 notificationGutsManager,
                 notificationLogger,
                 notificationEntryManager,
+                notificationRowContentBinder,
                 notificationInterruptionStateProvider,
                 notificationViewHierarchyManager,
                 keyguardViewMediator,
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index a1eccce..7108e65 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -70,6 +70,7 @@
 import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.DozeParameters;
@@ -146,6 +147,7 @@
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
             NotificationEntryManager notificationEntryManager,
+            NotificationRowContentBinder notificationRowContentBinder,
             NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             NotificationViewHierarchyManager notificationViewHierarchyManager,
             KeyguardViewMediator keyguardViewMediator,
@@ -232,6 +234,7 @@
                 notificationGutsManager,
                 notificationLogger,
                 notificationEntryManager,
+                notificationRowContentBinder,
                 notificationInterruptionStateProvider,
                 notificationViewHierarchyManager,
                 keyguardViewMediator,
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 1e19786..ec445d4 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -160,7 +160,7 @@
         final int userId = UserHandle.myUserId();
         final List<VolumeInfo> volumes = mStorageManager.getVolumes();
         for (VolumeInfo volume : volumes) {
-            if (!volume.isMountedReadable()) continue;
+            if (!volume.isMountedReadable() || volume.getMountUserId() != userId) continue;
 
             final String rootId;
             final String title;
@@ -192,9 +192,8 @@
                     title = mStorageManager.getBestVolumeDescription(privateVol);
                     storageUuid = StorageManager.convert(privateVol.fsUuid);
                 }
-            } else if ((volume.getType() == VolumeInfo.TYPE_PUBLIC
-                            || volume.getType() == VolumeInfo.TYPE_STUB)
-                    && volume.getMountUserId() == userId) {
+            } else if (volume.getType() == VolumeInfo.TYPE_PUBLIC
+                    || volume.getType() == VolumeInfo.TYPE_STUB) {
                 rootId = volume.getFsUuid();
                 title = mStorageManager.getBestVolumeDescription(volume);
                 storageUuid = null;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 5b4ef3a..70b56ed 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -194,6 +194,14 @@
         }
     }
 
+    void dispatchDeviceAttributesChanged() {
+        synchronized (mCallbacks) {
+            for (DeviceCallback callback : mCallbacks) {
+                callback.onDeviceAttributesChanged();
+            }
+        }
+    }
+
     /**
      * Stop scan MediaDevice
      */
@@ -306,14 +314,12 @@
             }
             mCurrentConnectedDevice = connectDevice;
             updatePhoneMediaDeviceSummary();
-            dispatchDeviceListUpdate();
+            dispatchDeviceAttributesChanged();
         }
 
         @Override
         public void onDeviceAttributesChanged() {
-            addPhoneDeviceIfNecessary();
-            removePhoneMediaDeviceIfNecessary();
-            dispatchDeviceListUpdate();
+            dispatchDeviceAttributesChanged();
         }
     }
 
@@ -327,7 +333,7 @@
          *
          * @param devices MediaDevice list
          */
-        void onDeviceListUpdate(List<MediaDevice> devices);
+        default void onDeviceListUpdate(List<MediaDevice> devices) {};
 
         /**
          * Callback for notifying the connected device is changed.
@@ -338,6 +344,12 @@
          * {@link MediaDeviceState#STATE_CONNECTING},
          * {@link MediaDeviceState#STATE_DISCONNECTED}
          */
-        void onSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state);
+        default void onSelectedDeviceStateChanged(MediaDevice device,
+                @MediaDeviceState int state) {};
+
+        /**
+         * Callback for notifying the device attributes is changed.
+         */
+        default void onDeviceAttributesChanged() {};
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 98bb74a..894aa78 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -355,7 +355,7 @@
         mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2);
 
         assertThat(mLocalMediaManager.getCurrentConnectedDevice()).isEqualTo(device2);
-        verify(mCallback).onDeviceListUpdate(any());
+        verify(mCallback).onDeviceAttributesChanged();
     }
 
     @Test
@@ -373,7 +373,7 @@
         mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_1);
 
-        verify(mCallback, never()).onDeviceListUpdate(any());
+        verify(mCallback, never()).onDeviceAttributesChanged();
     }
 
     @Test
@@ -382,6 +382,6 @@
 
         mLocalMediaManager.mMediaDeviceCallback.onDeviceAttributesChanged();
 
-        verify(mCallback).onDeviceListUpdate(any());
+        verify(mCallback).onDeviceAttributesChanged();
     }
 }
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index f40d3a1..96a98dc 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -8,6 +8,7 @@
     libs: [
         "telephony-common",
         "ims-common",
+        "unsupportedappusage",
     ],
     static_libs: [
         "junit",
@@ -40,6 +41,7 @@
     libs: [
         "android.test.base",
         "android.test.mock",
+        "unsupportedappusage",
     ],
     resource_dirs: ["res"],
     aaptflags: [
diff --git a/packages/SettingsProvider/res/values/overlayable.xml b/packages/SettingsProvider/res/values/overlayable.xml
deleted file mode 100644
index dc41a77..0000000
--- a/packages/SettingsProvider/res/values/overlayable.xml
+++ /dev/null
@@ -1,25 +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.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-  <overlayable name="SettingsToNotRestore">
-    <policy type="product|system|vendor">
-      <item type="array" name="restore_blocked_device_specific_settings" />
-      <item type="array" name="restore_blocked_global_settings" />
-      <item type="array" name="restore_blocked_secure_settings" />
-      <item type="array" name="restore_blocked_system_settings" />
-    </policy>
-  </overlayable>
-</resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 049b9f0..5636cc8 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -16,7 +16,7 @@
 
 package android.provider.settings.backup;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.provider.Settings;
 
 /** Information relating to the Secure settings which should be backed up */
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 89b19de..3f5b0da 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -16,7 +16,7 @@
 
 package android.provider.settings.backup;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.provider.Settings;
 
 /** Information about the system settings to back up */
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 94ab0f1..c5d4fa9 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -25,7 +25,7 @@
 import static android.provider.settings.validators.SettingsValidators.VIBRATION_INTENSITY_VALIDATOR;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.hardware.display.ColorDisplayManager;
 import android.os.BatteryManager;
diff --git a/packages/SystemUI/res/layout/people_strip.xml b/packages/SystemUI/res/layout/people_strip.xml
index f0ac08b..982aa8e 100644
--- a/packages/SystemUI/res/layout/people_strip.xml
+++ b/packages/SystemUI/res/layout/people_strip.xml
@@ -18,7 +18,10 @@
 <com.android.systemui.statusbar.notification.stack.PeopleHubView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="105dp">
+    android:layout_height="@dimen/notification_section_header_height"
+    android:focusable="true"
+    android:clickable="true"
+>
 
     <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
         android:id="@+id/backgroundNormal"
@@ -34,199 +37,56 @@
         android:id="@+id/people_list"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingTop="12dp"
-        android:paddingBottom="12dp"
         android:gravity="center"
         android:orientation="horizontal">
 
-        <View
-            android:layout_width="8dp"
-            android:layout_height="match_parent"
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
             android:layout_weight="1"
+            android:layout_marginStart="@dimen/notification_section_header_padding_left"
+            android:gravity="start"
+            android:textAlignment="gravity"
+            android:text="@string/notification_section_header_conversations"
+            android:textSize="12sp"
+            android:textColor="@color/notification_section_header_label_color"
+            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
         />
 
-        <LinearLayout
-            android:layout_width="70dp"
-            android:layout_height="match_parent"
-            android:gravity="center_horizontal"
-            android:orientation="vertical"
-            android:visibility="invisible">
-
-            <ImageView
-                android:id="@+id/person_icon"
-                android:layout_width="36dp"
-                android:layout_height="36dp"
-                android:scaleType="fitCenter"
-            />
-
-            <TextView
-                android:id="@+id/person_name"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:paddingTop="8dp"
-                android:ellipsize="end"
-                android:maxLines="2"
-                android:textAlignment="center"
-            />
-
-        </LinearLayout>
-
-        <View
-            android:layout_width="8dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
+        <ImageView
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:padding="8dp"
+            android:scaleType="fitCenter"
         />
 
-        <View
-            android:layout_width="8dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
+        <ImageView
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:padding="8dp"
+            android:scaleType="fitCenter"
         />
 
-        <LinearLayout
-            android:layout_width="70dp"
-            android:layout_height="match_parent"
-            android:gravity="center_horizontal"
-            android:orientation="vertical"
-            android:visibility="invisible">
-
-            <ImageView
-                android:id="@+id/person_icon"
-                android:layout_width="36dp"
-                android:layout_height="36dp"
-                android:scaleType="fitCenter"
-            />
-
-            <TextView
-                android:id="@+id/person_name"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:paddingTop="8dp"
-                android:ellipsize="end"
-                android:maxLines="2"
-                android:textAlignment="center"
-            />
-
-        </LinearLayout>
-
-        <View
-            android:layout_width="8dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
+        <ImageView
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:padding="8dp"
+            android:scaleType="fitCenter"
         />
 
-        <View
-            android:layout_width="8dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
+        <ImageView
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:padding="8dp"
+            android:scaleType="fitCenter"
         />
 
-        <LinearLayout
-            android:layout_width="70dp"
-            android:layout_height="match_parent"
-            android:gravity="center_horizontal"
-            android:orientation="vertical"
-            android:visibility="invisible">
-
-            <ImageView
-                android:id="@+id/person_icon"
-                android:layout_width="36dp"
-                android:layout_height="36dp"
-                android:scaleType="fitCenter"
-            />
-
-            <TextView
-                android:id="@+id/person_name"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:paddingTop="8dp"
-                android:ellipsize="end"
-                android:maxLines="2"
-                android:textAlignment="center"
-            />
-
-        </LinearLayout>
-
-        <View
-            android:layout_width="8dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-        />
-
-        <View
-            android:layout_width="8dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-        />
-
-        <LinearLayout
-            android:layout_width="70dp"
-            android:layout_height="match_parent"
-            android:gravity="center_horizontal"
-            android:orientation="vertical"
-            android:visibility="invisible">
-
-            <ImageView
-                android:id="@+id/person_icon"
-                android:layout_width="36dp"
-                android:layout_height="36dp"
-                android:scaleType="fitCenter"
-            />
-
-            <TextView
-                android:id="@+id/person_name"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:paddingTop="8dp"
-                android:ellipsize="end"
-                android:maxLines="2"
-                android:textAlignment="center"
-            />
-
-        </LinearLayout>
-
-        <View
-            android:layout_width="8dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-        />
-
-        <View
-            android:layout_width="8dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-        />
-
-        <LinearLayout
-            android:layout_width="70dp"
-            android:layout_height="match_parent"
-            android:gravity="center_horizontal"
-            android:orientation="vertical"
-            android:visibility="invisible">
-
-            <ImageView
-                android:id="@+id/person_icon"
-                android:layout_width="36dp"
-                android:layout_height="36dp"
-                android:scaleType="fitCenter"
-            />
-
-            <TextView
-                android:id="@+id/person_name"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:paddingTop="8dp"
-                android:ellipsize="end"
-                android:maxLines="2"
-                android:textAlignment="center"
-            />
-
-        </LinearLayout>
-
-        <View
-            android:layout_width="8dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
+        <ImageView
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_marginEnd="8dp"
+            android:padding="8dp"
+            android:scaleType="fitCenter"
         />
 
     </LinearLayout>
@@ -236,4 +96,4 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
-</com.android.systemui.statusbar.notification.stack.PeopleHubView>
\ No newline at end of file
+</com.android.systemui.statusbar.notification.stack.PeopleHubView>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 52c0a26..4f532b7 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1163,6 +1163,9 @@
     <!-- Section title for notifications that do not vibrate or make noise. [CHAR LIMIT=40] -->
     <string name="notification_section_header_gentle">Silent notifications</string>
 
+    <!-- Section title for conversational notifications. [CHAR LIMIT=40] -->
+    <string name="notification_section_header_conversations">Conversations</string>
+
     <!-- Content description for accessibility: Tapping this button will dismiss all gentle notifications [CHAR LIMIT=NONE] -->
     <string name="accessibility_notification_section_header_gentle_clear_all">Clear all silent notifications</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index eecc54c..bbe972d 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -121,6 +121,7 @@
 import com.android.systemui.util.leak.LeakDetector;
 import com.android.systemui.util.leak.LeakReporter;
 import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.wm.DisplayImeController;
 import com.android.systemui.wm.DisplayWindowController;
 import com.android.systemui.wm.SystemWindows;
 
@@ -321,6 +322,7 @@
     @Inject Lazy<StatusBar> mStatusBar;
     @Inject Lazy<DisplayWindowController> mDisplayWindowController;
     @Inject Lazy<SystemWindows> mSystemWindows;
+    @Inject Lazy<DisplayImeController> mDisplayImeController;
 
     @Inject
     public Dependency() {
@@ -509,6 +511,7 @@
         mProviders.put(StatusBar.class, mStatusBar::get);
         mProviders.put(DisplayWindowController.class, mDisplayWindowController::get);
         mProviders.put(SystemWindows.class, mSystemWindows::get);
+        mProviders.put(DisplayImeController.class, mDisplayImeController::get);
 
         // TODO(b/118592525): to support multi-display , we start to add something which is
         //                    per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 7cd29ea..d835ee1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -344,11 +344,14 @@
         mSavedBubbleKeysPerUser = new SparseSetArray<>();
         mCurrentUserId = mNotifUserManager.getCurrentUserId();
         mNotifUserManager.addUserChangedListener(
-                newUserId -> {
-                    saveBubbles(mCurrentUserId);
-                    mBubbleData.dismissAll(DISMISS_USER_CHANGED);
-                    restoreBubbles(newUserId);
-                    mCurrentUserId = newUserId;
+                new NotificationLockscreenUserManager.UserChangedListener() {
+                    @Override
+                    public void onUserChanged(int newUserId) {
+                        BubbleController.this.saveBubbles(mCurrentUserId);
+                        mBubbleData.dismissAll(DISMISS_USER_CHANGED);
+                        BubbleController.this.restoreBubbles(newUserId);
+                        mCurrentUserId = newUserId;
+                    }
                 });
 
         mUserCreatedBubbles = new HashSet<>();
@@ -743,9 +746,16 @@
             return !isAutogroupSummary;
         } else {
             // If it's not a user dismiss it's a cancel.
+            for (int i = 0; i < bubbleChildren.size(); i++) {
+                // First check if any of these are user-created (i.e. experimental bubbles)
+                if (mUserCreatedBubbles.contains(bubbleChildren.get(i).getKey())) {
+                    // Experimental bubble! Intercept the removal.
+                    return true;
+                }
+            }
+            // Not an experimental bubble, safe to remove.
             mBubbleData.removeSuppressedSummary(groupKey);
-
-            // Remove any associated bubble children.
+            // Remove any associated bubble children with the summary.
             for (int i = 0; i < bubbleChildren.size(); i++) {
                 Bubble bubbleChild = bubbleChildren.get(i);
                 mBubbleData.notificationEntryRemoved(bubbleChild.getEntry(),
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 8d10552..53a23b8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -20,7 +20,6 @@
 
 import android.app.INotificationManager;
 import android.content.Context;
-import android.content.pm.IPackageManager;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.display.NightDisplayListener;
 import android.os.Handler;
@@ -115,13 +114,6 @@
     /** */
     @Singleton
     @Provides
-    public IPackageManager provideIPackageManager() {
-        return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
-    }
-
-    /** */
-    @Singleton
-    @Provides
     public LayoutInflater providerLayoutInflater(Context context) {
         return LayoutInflater.from(context);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index bb12b53..26337b1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -27,6 +27,7 @@
 import android.app.WallpaperManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
+import android.content.pm.IPackageManager;
 import android.content.res.Resources;
 import android.hardware.SensorPrivacyManager;
 import android.os.Handler;
@@ -123,6 +124,13 @@
         return WindowManagerGlobal.getWindowManagerService();
     }
 
+    /** */
+    @Singleton
+    @Provides
+    public IPackageManager provideIPackageManager() {
+        return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+    }
+
     @Singleton
     @Provides
     static KeyguardManager provideKeyguardManager(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 2005d79..ac05c53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -40,7 +40,7 @@
  * You will probably need to restart systemui for the changes to be picked up:
  *
  * {@code
- *  $ adb shell am crash com.android.systemui
+ *  $ adb shell am restart com.android.systemui
  * }
  */
 @Singleton
@@ -59,6 +59,11 @@
         return getDeviceConfigFlag("notification.newpipeline.enabled", false);
     }
 
+    public boolean isNewNotifPipelineRenderingEnabled() {
+        return isNewNotifPipelineEnabled()
+                && getDeviceConfigFlag("notification.newpipeline.rendering", false);
+    }
+
     private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
         synchronized (mCachedDeviceConfigFlags) {
             for (String key : properties.getKeyset()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index ff4ce94..ebf7c2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -49,6 +49,12 @@
     /** Adds a listener to be notified when the current user changes. */
     void addUserChangedListener(UserChangedListener listener);
 
+    /**
+     * Removes a listener previously registered with
+     * {@link #addUserChangedListener(UserChangedListener)}
+     */
+    void removeUserChangedListener(UserChangedListener listener);
+
     SparseArray<UserInfo> getCurrentProfiles();
 
     void setLockscreenPublicMode(boolean isProfilePublic, int userId);
@@ -79,6 +85,7 @@
 
     /** Notified when the current user changes. */
     interface UserChangedListener {
-        void onUserChanged(int userId);
+        default void onUserChanged(int userId) {}
+        default void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 2e369b3..976531d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -117,51 +117,63 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-                updateCurrentProfilesCache();
-                Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
+            switch (action) {
+                case Intent.ACTION_USER_SWITCHED:
+                    mCurrentUserId = intent.getIntExtra(
+                            Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL);
+                    updateCurrentProfilesCache();
 
-                updateLockscreenNotificationSetting();
-                updatePublicMode();
-                // The filtering needs to happen before the update call below in order to make sure
-                // the presenter has the updated notifications from the new user
-                getEntryManager().reapplyFilterAndSort("user switched");
-                mPresenter.onUserSwitched(mCurrentUserId);
+                    Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
 
-                for (UserChangedListener listener : mListeners) {
-                    listener.onUserChanged(mCurrentUserId);
-                }
-            } else if (Intent.ACTION_USER_ADDED.equals(action)) {
-                updateCurrentProfilesCache();
-            } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
-                // Start the overview connection to the launcher service
-                Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
-            } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
-                final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
-                final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
-                if (intentSender != null) {
-                    try {
-                        mContext.startIntentSender(intentSender, null, 0, 0, 0);
-                    } catch (IntentSender.SendIntentException e) {
-                        /* ignore */
+                    updateLockscreenNotificationSetting();
+                    updatePublicMode();
+                    // The filtering needs to happen before the update call below in order to
+                    // make sure
+                    // the presenter has the updated notifications from the new user
+                    getEntryManager().reapplyFilterAndSort("user switched");
+                    mPresenter.onUserSwitched(mCurrentUserId);
+
+                    for (UserChangedListener listener : mListeners) {
+                        listener.onUserChanged(mCurrentUserId);
                     }
-                }
-                if (notificationKey != null) {
-                    NotificationEntry entry =
-                            getEntryManager().getActiveNotificationUnfiltered(notificationKey);
-                    final int count = getEntryManager().getActiveNotificationsCount();
-                    final int rank = entry != null ? entry.getRanking().getRank() : 0;
-                    NotificationVisibility.NotificationLocation location =
-                            NotificationLogger.getNotificationLocation(entry);
-                    final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
-                            rank, count, true, location);
-                    try {
-                        mBarService.onNotificationClick(notificationKey, nv);
-                    } catch (RemoteException exception) {
-                        /* ignore */
+                    break;
+                case Intent.ACTION_USER_ADDED:
+                case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
+                case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
+                    updateCurrentProfilesCache();
+                    break;
+                case Intent.ACTION_USER_UNLOCKED:
+                    // Start the overview connection to the launcher service
+                    Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
+                    break;
+                case NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION:
+                    final IntentSender intentSender = intent.getParcelableExtra(
+                            Intent.EXTRA_INTENT);
+                    final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
+                    if (intentSender != null) {
+                        try {
+                            mContext.startIntentSender(intentSender, null, 0, 0, 0);
+                        } catch (IntentSender.SendIntentException e) {
+                            /* ignore */
+                        }
                     }
-                }
+                    if (notificationKey != null) {
+                        NotificationEntry entry =
+                                getEntryManager().getActiveNotificationUnfiltered(notificationKey);
+                        final int count = getEntryManager().getActiveNotificationsCount();
+                        final int rank = entry != null ? entry.getRanking().getRank() : 0;
+                        NotificationVisibility.NotificationLocation location =
+                                NotificationLogger.getNotificationLocation(entry);
+                        final NotificationVisibility nv = NotificationVisibility.obtain(
+                                notificationKey,
+                                rank, count, true, location);
+                        try {
+                            mBarService.onNotificationClick(notificationKey, nv);
+                        } catch (RemoteException exception) {
+                            /* ignore */
+                        }
+                    }
+                    break;
             }
         }
     };
@@ -266,6 +278,8 @@
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_UNLOCKED);
+        filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+        filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
         mBroadcastDispatcher.registerReceiver(mBaseBroadcastReceiver, filter);
 
         IntentFilter internalFilter = new IntentFilter();
@@ -489,6 +503,11 @@
                 }
             }
         }
+        mMainHandler.post(() -> {
+            for (UserChangedListener listener : mListeners) {
+                listener.onCurrentProfilesChanged(mCurrentProfiles);
+            }
+        });
     }
 
     public boolean isAnyProfilePublicMode() {
@@ -555,6 +574,11 @@
         mListeners.add(listener);
     }
 
+    @Override
+    public void removeUserChangedListener(UserChangedListener listener) {
+        mListeners.remove(listener);
+    }
+
 //    public void updatePublicMode() {
 //        //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns
 //        // false when it should be true. Therefore, if we are not on the SHADE, don't even bother
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 43d0399..667e721 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -584,7 +584,15 @@
 
     public void bindRow(ExpandableNotificationRow row) {
         row.setRemoteInputController(mRemoteInputController);
-        row.setRemoteViewClickHandler(mOnClickHandler);
+    }
+
+    /**
+     * Return on-click handler for notification remote views
+     *
+     * @return on-click handler
+     */
+    public RemoteViews.OnClickHandler getRemoteViewsOnClickHandler() {
+        return mOnClickHandler;
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
index d1f6ebf..ec8dbea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 
+import com.android.systemui.statusbar.notification.row.NotificationRowModule;
+
 import javax.inject.Singleton;
 
 import dagger.Module;
@@ -26,7 +28,7 @@
 /**
  * Dagger Module providing common dependencies of StatusBar.
  */
-@Module
+@Module(includes = {NotificationRowModule.class})
 public class StatusBarDependenciesModule {
     /**
      * Provides our instance of CommandQueue which is considered optional.
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 33d97a1..a2578ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -34,6 +34,7 @@
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
@@ -131,6 +132,7 @@
     private final KeyguardEnvironment mKeyguardEnvironment;
     private final NotificationGroupManager mGroupManager;
     private final NotificationRankingManager mRankingManager;
+    private final FeatureFlags mFeatureFlags;
 
     private NotificationPresenter mPresenter;
     private RankingMap mLatestRankingMap;
@@ -170,11 +172,13 @@
             NotifLog notifLog,
             NotificationGroupManager groupManager,
             NotificationRankingManager rankingManager,
-            KeyguardEnvironment keyguardEnvironment) {
+            KeyguardEnvironment keyguardEnvironment,
+            FeatureFlags featureFlags) {
         mNotifLog = notifLog;
         mGroupManager = groupManager;
         mRankingManager = rankingManager;
         mKeyguardEnvironment = keyguardEnvironment;
+        mFeatureFlags = featureFlags;
     }
 
     /** Once called, the NEM will start processing notification events from system server. */
@@ -290,6 +294,10 @@
      * WARNING: this will call back into us.  Don't hold any locks.
      */
     @Override
+    public void handleInflationException(NotificationEntry n, Exception e) {
+        handleInflationException(n.getSbn(), e);
+    }
+
     public void handleInflationException(StatusBarNotification n, Exception e) {
         removeNotificationInternal(
                 n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */,
@@ -529,9 +537,12 @@
         NotificationEntry entry = new NotificationEntry(notification, ranking);
 
         Dependency.get(LeakDetector.class).trackInstance(entry);
+
         // Construct the expanded view.
-        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
-                REASON_CANCEL));
+        if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+            requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
+                    REASON_CANCEL));
+        }
 
         abortExistingInflation(key, "addNotification");
         mPendingNotifications.put(key, entry);
@@ -574,8 +585,11 @@
             listener.onPreEntryUpdated(entry);
         }
 
-        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
-                REASON_CANCEL));
+        if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+            requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
+                    REASON_CANCEL));
+        }
+
         updateNotifications("updateNotificationInternal");
 
         if (DEBUG) {
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 873cdbc..856b75b 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
@@ -299,6 +299,14 @@
             if (!isLifetimeExtended(entry)) {
                 Ranking ranking = requireRanking(rankingMap, entry.getKey());
                 entry.setRanking(ranking);
+
+                // TODO: (b/145659174) update the sbn's overrideGroupKey in
+                //  NotificationEntry.setRanking instead of here once we fully migrate to the
+                //  NewNotifPipeline
+                final String newOverrideGroupKey = ranking.getOverrideGroupKey();
+                if (!Objects.equals(entry.getSbn().getOverrideGroupKey(), newOverrideGroupKey)) {
+                    entry.getSbn().setOverrideGroupKey(newOverrideGroupKey);
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflater.java
new file mode 100644
index 0000000..fc04827
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflater.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+import com.android.systemui.statusbar.notification.collection.coordinator.PreparationCoordinator;
+
+/**
+ * Used by the {@link PreparationCoordinator}.  When notifications are added or updated, the
+ * NotifInflater is asked to (re)inflated and prepare their views.  This inflation occurs off the
+ * main thread. When the inflation is finished, NotifInflater will trigger its InflationCallback.
+ */
+public interface NotifInflater {
+
+    /**
+     * Callback used when inflation is finished.
+     */
+    void setInflationCallback(InflationCallback callback);
+
+    /**
+     * Called to rebind the entry's views.
+     */
+    void rebindViews(NotificationEntry entry);
+
+    /**
+     * Called to inflate the views of an entry.  Views are not considered inflated until all of its
+     * views are bound. Once all views are inflated, the InflationCallback is triggered.
+     */
+    void inflateViews(NotificationEntry entry);
+
+    /**
+     * Request to stop the inflation of an entry.  For example, called when a notification is
+     * removed and no longer needs to be inflated.
+     */
+    void abortInflation(NotificationEntry entry);
+
+    /**
+     * Callback once all the views are inflated and bound for a given NotificationEntry.
+     */
+    interface InflationCallback {
+        void onInflationFinished(NotificationEntry entry);
+    }
+}
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
new file mode 100644
index 0000000..0d17557
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -0,0 +1,158 @@
+/*
+ * 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;
+
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+
+import android.os.RemoteException;
+import android.service.notification.NotificationStats;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Handles notification inflating, rebinding, and inflation aborting.
+ *
+ * Currently a wrapper for NotificationRowBinderImpl.
+ */
+@Singleton
+public class NotifInflaterImpl implements NotifInflater {
+
+    private final IStatusBarService mStatusBarService;
+    private final NotifCollection mNotifCollection;
+
+    private NotificationRowBinderImpl mNotificationRowBinder;
+    private InflationCallback mExternalInflationCallback;
+
+    @Inject
+    public NotifInflaterImpl(
+            IStatusBarService statusBarService,
+            NotifCollection notifCollection) {
+        mStatusBarService = statusBarService;
+        mNotifCollection = notifCollection;
+    }
+
+    /**
+     * Attaches the row binder for inflation.
+     */
+    public void setRowBinder(NotificationRowBinderImpl rowBinder) {
+        mNotificationRowBinder = rowBinder;
+        mNotificationRowBinder.setInflationCallback(mInflationCallback);
+    }
+
+    @Override
+    public void setInflationCallback(InflationCallback callback) {
+        mExternalInflationCallback = callback;
+    }
+
+    @Override
+    public void rebindViews(NotificationEntry entry) {
+        inflateViews(entry);
+    }
+
+    /**
+     * Called to inflate the views of an entry.  Views are not considered inflated until all of its
+     * views are bound.
+     */
+    @Override
+    public void inflateViews(NotificationEntry entry) {
+        try {
+            entry.setHasInflationError(false);
+            requireBinder().inflateViews(entry, getDismissCallback(entry));
+        } catch (InflationException e) {
+            // logged in mInflationCallback.handleInflationException
+        }
+    }
+
+    @Override
+    public void abortInflation(NotificationEntry entry) {
+        entry.abortTask();
+    }
+
+    private Runnable getDismissCallback(NotificationEntry entry) {
+        return new Runnable() {
+            @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,
+                                NotificationVisibility.obtain(entry.getKey(),
+                                        entry.getRanking().getRank(),
+                                        mNotifCollection.getNotifs().size(),
+                                        true,
+                                        NotificationLogger.getNotificationLocation(entry))
+                        ));
+            }
+        };
+    }
+
+    private NotificationRowBinderImpl requireBinder() {
+        if (mNotificationRowBinder == null) {
+            throw new RuntimeException("NotificationRowBinder must be attached before using "
+                    + "NotifInflaterImpl.");
+        }
+        return mNotificationRowBinder;
+    }
+
+    private final NotificationContentInflater.InflationCallback mInflationCallback =
+            new NotificationContentInflater.InflationCallback() {
+                @Override
+                public void handleInflationException(
+                        NotificationEntry entry,
+                        Exception e) {
+                    entry.setHasInflationError(true);
+                    try {
+                        final StatusBarNotification sbn = entry.getSbn();
+                        // report notification inflation errors back up
+                        // to notification delegates
+                        mStatusBarService.onNotificationError(
+                                sbn.getPackageName(),
+                                sbn.getTag(),
+                                sbn.getId(),
+                                sbn.getUid(),
+                                sbn.getInitialPid(),
+                                e.getMessage(),
+                                sbn.getUserId());
+                    } catch (RemoteException ex) {
+                    }
+                }
+
+                @Override
+                public void onAsyncInflationFinished(
+                        NotificationEntry entry,
+                        int inflatedFlags) {
+                    if (mExternalInflationCallback != null) {
+                        mExternalInflationCallback.onInflationFinished(entry);
+                    }
+                }
+            };
+}
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 4f4fb24..28e486d 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
@@ -102,6 +102,9 @@
     /** 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 **/
+    private boolean mHasInflationError;
+
 
     /*
     * Old members
@@ -576,6 +579,18 @@
         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;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
index 6c93618..20f206b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
@@ -41,8 +41,8 @@
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -65,6 +65,7 @@
             Dependency.get(NotificationInterruptionStateProvider.class);
 
     private final Context mContext;
+    private final NotificationRowContentBinder mRowContentBinder;
     private final NotificationMessagingUtil mMessagingUtil;
     private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
             this::logNotificationExpansion;
@@ -76,7 +77,7 @@
     private NotificationPresenter mPresenter;
     private NotificationListContainer mListContainer;
     private HeadsUpManager mHeadsUpManager;
-    private NotificationContentInflater.InflationCallback mInflationCallback;
+    private NotificationRowContentBinder.InflationCallback mInflationCallback;
     private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
     private BindRowCallback mBindRowCallback;
     private NotificationClicker mNotificationClicker;
@@ -84,11 +85,13 @@
 
     public NotificationRowBinderImpl(
             Context context,
+            NotificationRowContentBinder rowContentBinder,
             boolean allowLongPress,
             KeyguardBypassController keyguardBypassController,
             StatusBarStateController statusBarStateController,
             NotificationLogger logger) {
         mContext = context;
+        mRowContentBinder = rowContentBinder;
         mMessagingUtil = new NotificationMessagingUtil(context);
         mAllowLongPress = allowLongPress;
         mKeyguardBypassController = keyguardBypassController;
@@ -109,16 +112,18 @@
     public void setUpWithPresenter(NotificationPresenter presenter,
             NotificationListContainer listContainer,
             HeadsUpManager headsUpManager,
-            NotificationContentInflater.InflationCallback inflationCallback,
             BindRowCallback bindRowCallback) {
         mPresenter = presenter;
         mListContainer = listContainer;
         mHeadsUpManager = headsUpManager;
-        mInflationCallback = inflationCallback;
         mBindRowCallback = bindRowCallback;
         mOnAppOpsClickListener = mGutsManager::openGuts;
     }
 
+    public void setInflationCallback(NotificationRowContentBinder.InflationCallback callback) {
+        mInflationCallback = callback;
+    }
+
     public void setNotificationClicker(NotificationClicker clicker) {
         mNotificationClicker = clicker;
     }
@@ -154,19 +159,6 @@
     private void bindRow(NotificationEntry entry, PackageManager pmUser,
             StatusBarNotification sbn, ExpandableNotificationRow row,
             Runnable onDismissRunnable) {
-        row.setExpansionLogger(mExpansionLogger, entry.getSbn().getKey());
-        row.setBypassController(mKeyguardBypassController);
-        row.setStatusBarStateController(mStatusBarStateController);
-        row.setGroupManager(mGroupManager);
-        row.setHeadsUpManager(mHeadsUpManager);
-        row.setOnExpandClickListener(mPresenter);
-        row.setInflationCallback(mInflationCallback);
-        if (mAllowLongPress) {
-            row.setLongPressListener(mGutsManager::openGuts);
-        }
-        mListContainer.bindRow(row);
-        getRemoteInputManager().bindRow(row);
-
         // Get the app name.
         // Note that Notification.Builder#bindHeaderAppName has similar logic
         // but since this field is used in the guts, it must be accurate.
@@ -184,15 +176,33 @@
         } catch (PackageManager.NameNotFoundException e) {
             // Do nothing
         }
-        row.setAppName(appname);
+
+        row.initialize(
+                appname,
+                sbn.getKey(),
+                mExpansionLogger,
+                mKeyguardBypassController,
+                mGroupManager,
+                mHeadsUpManager,
+                mRowContentBinder,
+                mPresenter);
+
+        // TODO: Either move these into ExpandableNotificationRow#initialize or out of row entirely
+        row.setStatusBarStateController(mStatusBarStateController);
+        row.setInflationCallback(mInflationCallback);
+        row.setAppOpsOnClickListener(mOnAppOpsClickListener);
+        if (mAllowLongPress) {
+            row.setLongPressListener(mGutsManager::openGuts);
+        }
+        mListContainer.bindRow(row);
+        getRemoteInputManager().bindRow(row);
+
         row.setOnDismissRunnable(onDismissRunnable);
         row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
         if (ENABLE_REMOTE_INPUT) {
             row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
         }
 
-        row.setAppOpsOnClickListener(mOnAppOpsClickListener);
-
         mBindRowCallback.onBindRow(entry, pmUser, sbn, row);
     }
 
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 13247193..eeb54ab 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
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.collection.coordinator;
 
 import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender;
@@ -45,14 +46,19 @@
      */
     @Inject
     public NotifCoordinators(
+            FeatureFlags featureFlags,
             KeyguardCoordinator keyguardCoordinator,
             RankingCoordinator rankingCoordinator,
             ForegroundCoordinator foregroundCoordinator,
-            DeviceProvisionedCoordinator deviceProvisionedCoordinator) {
+            DeviceProvisionedCoordinator deviceProvisionedCoordinator,
+            PreparationCoordinator preparationCoordinator) {
         mCoordinators.add(keyguardCoordinator);
         mCoordinators.add(rankingCoordinator);
         mCoordinators.add(foregroundCoordinator);
         mCoordinators.add(deviceProvisionedCoordinator);
+        if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
+            mCoordinators.add(preparationCoordinator);
+        }
         // TODO: add new Coordinators here! (b/145134683, b/112656837)
     }
 
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
new file mode 100644
index 0000000..a14f0e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -0,0 +1,131 @@
+/*
+ * 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 com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.NotifInflater;
+import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.logging.NotifEvent;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Kicks off notification inflation and view rebinding when a notification is added or updated.
+ * Aborts inflation when a notification is removed.
+ *
+ * If a notification is not done inflating, this coordinator will filter the notification out
+ * from the NotifListBuilder.
+ */
+@Singleton
+public class PreparationCoordinator implements Coordinator {
+    private static final String TAG = "PreparationCoordinator";
+
+    private final NotifLog mNotifLog;
+    private final NotifInflater mNotifInflater;
+    private final List<NotificationEntry> mPendingNotifications = new ArrayList<>();
+
+    @Inject
+    public PreparationCoordinator(NotifLog notifLog, NotifInflaterImpl notifInflater) {
+        mNotifLog = notifLog;
+        mNotifInflater = notifInflater;
+        mNotifInflater.setInflationCallback(mInflationCallback);
+    }
+
+    @Override
+    public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+        notifCollection.addCollectionListener(mNotifCollectionListener);
+        notifListBuilder.addPreRenderFilter(mNotifInflationErrorFilter);
+        notifListBuilder.addPreRenderFilter(mNotifInflatingFilter);
+    }
+
+    private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
+        @Override
+        public void onEntryAdded(NotificationEntry entry) {
+            inflateEntry(entry, "entryAdded");
+        }
+
+        @Override
+        public void onEntryUpdated(NotificationEntry entry) {
+            rebind(entry, "entryUpdated");
+        }
+
+        @Override
+        public void onEntryRemoved(NotificationEntry entry, int reason, boolean removedByUser) {
+            abortInflation(entry, "entryRemoved reason=" + reason);
+        }
+    };
+
+    private final NotifFilter mNotifInflationErrorFilter = new NotifFilter(
+            TAG + "InflationError") {
+        /**
+         * Filters out notifications that threw an error when attempting to inflate.
+         */
+        @Override
+        public boolean shouldFilterOut(NotificationEntry entry, long now) {
+            if (entry.hasInflationError()) {
+                mPendingNotifications.remove(entry);
+                return true;
+            }
+            return false;
+        }
+    };
+
+    private final NotifFilter mNotifInflatingFilter = new NotifFilter(TAG + "Inflating") {
+        /**
+         * Filters out notifications that haven't been inflated yet
+         */
+        @Override
+        public boolean shouldFilterOut(NotificationEntry entry, long now) {
+            return mPendingNotifications.contains(entry);
+        }
+    };
+
+    private final NotifInflater.InflationCallback mInflationCallback =
+            new NotifInflater.InflationCallback() {
+        @Override
+        public void onInflationFinished(NotificationEntry entry) {
+            mNotifLog.log(NotifEvent.INFLATED, entry);
+            mPendingNotifications.remove(entry);
+            mNotifInflatingFilter.invalidateList();
+        }
+    };
+
+    private void inflateEntry(NotificationEntry entry, String reason) {
+        abortInflation(entry, reason);
+        mPendingNotifications.add(entry);
+        mNotifInflater.inflateViews(entry);
+    }
+
+    private void rebind(NotificationEntry entry, String reason) {
+        mNotifInflater.rebindViews(entry);
+    }
+
+    private void abortInflation(NotificationEntry entry, String reason) {
+        mNotifLog.log(NotifEvent.INFLATION_ABORTED, reason);
+        entry.abortTask();
+        mPendingNotifications.remove(entry);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
index 5e0bd4f..8d3d0ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
@@ -20,9 +20,12 @@
 
 import com.android.systemui.DumpController;
 import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
 import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators;
 import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer;
 
@@ -41,7 +44,9 @@
     private final NotifCollection mNotifCollection;
     private final NotifListBuilderImpl mNotifPipeline;
     private final NotifCoordinators mNotifPluggableCoordinators;
+    private final NotifInflaterImpl mNotifInflater;
     private final DumpController mDumpController;
+    private final FeatureFlags mFeatureFlags;
 
     private final FakePipelineConsumer mFakePipelineConsumer = new FakePipelineConsumer();
 
@@ -51,20 +56,30 @@
             NotifCollection notifCollection,
             NotifListBuilderImpl notifPipeline,
             NotifCoordinators notifCoordinators,
-            DumpController dumpController) {
+            NotifInflaterImpl notifInflater,
+            DumpController dumpController,
+            FeatureFlags featureFlags) {
         mGroupCoalescer = groupCoalescer;
         mNotifCollection = notifCollection;
         mNotifPipeline = notifPipeline;
         mNotifPluggableCoordinators = notifCoordinators;
         mDumpController = dumpController;
+        mNotifInflater = notifInflater;
+        mFeatureFlags = featureFlags;
     }
 
     /** Hooks the new pipeline up to NotificationManager */
     public void initialize(
-            NotificationListener notificationService) {
+            NotificationListener notificationService,
+            NotificationRowBinderImpl rowBinder) {
 
         mDumpController.registerDumpable("NotifPipeline", this);
 
+        // Setup inflation
+        if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+            mNotifInflater.setRowBinder(rowBinder);
+        }
+
         // Wire up coordinators
         mFakePipelineConsumer.attach(mNotifPipeline);
         mNotifPluggableCoordinators.attach(mNotifCollection, mNotifPipeline);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 3e1b5bd..89e5f55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -294,6 +294,9 @@
         }
     }
 
+    /**
+     * Logs Notification inflation error
+     */
     private void logNotificationError(
             StatusBarNotification notification,
             Exception exception) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
index 2c0c942..e81d361 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
@@ -37,7 +37,8 @@
     val key: PersonKey,
     val name: CharSequence,
     val avatar: Drawable,
-    val clickIntent: PendingIntent
+    val clickIntent: PendingIntent,
+    val userId: Int
 )
 
 /** Unique identifier for a Person in PeopleHub. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
index 784673e..88b4147 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
@@ -17,28 +17,27 @@
 package com.android.systemui.statusbar.notification.people
 
 import android.app.Notification
-import android.content.Context
-import android.graphics.Canvas
-import android.graphics.ColorFilter
-import android.graphics.PixelFormat
-import android.graphics.drawable.BitmapDrawable
+import android.content.pm.UserInfo
 import android.graphics.drawable.Drawable
-import android.os.UserHandle
+import android.os.UserManager
 import android.service.notification.StatusBarNotification
-import android.util.TypedValue
+import android.util.SparseArray
 import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
 import com.android.internal.statusbar.NotificationVisibility
 import com.android.internal.widget.MessagingGroup
-import com.android.launcher3.icons.BaseIconFactory
 import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.NotificationPersonExtractorPlugin
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.notification.NotificationEntryListener
 import com.android.systemui.statusbar.notification.NotificationEntryManager
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.policy.ExtensionController
 import java.util.ArrayDeque
+import java.util.concurrent.Executor
 import javax.inject.Inject
 import javax.inject.Singleton
 
@@ -52,8 +51,7 @@
 
 @Singleton
 class NotificationPersonExtractorPluginBoundary @Inject constructor(
-    extensionController: ExtensionController,
-    private val context: Context
+    extensionController: ExtensionController
 ) : NotificationPersonExtractor {
 
     private var plugin: NotificationPersonExtractorPlugin? = null
@@ -70,9 +68,8 @@
     }
 
     override fun extractPerson(sbn: StatusBarNotification) =
-            plugin?.extractPerson(sbn)?.let { data ->
-                val badged = addBadgeToDrawable(data.avatar, context, sbn.packageName, sbn.user)
-                PersonModel(data.key, data.name, badged, data.clickIntent)
+            plugin?.extractPerson(sbn)?.run {
+                PersonModel(key, name, avatar, clickIntent, sbn.user.identifier)
             }
 
     override fun extractPersonKey(sbn: StatusBarNotification) = plugin?.extractPersonKey(sbn)
@@ -84,11 +81,16 @@
 @Singleton
 class PeopleHubDataSourceImpl @Inject constructor(
     private val notificationEntryManager: NotificationEntryManager,
-    private val peopleHubManager: PeopleHubManager,
-    private val extractor: NotificationPersonExtractor
+    private val extractor: NotificationPersonExtractor,
+    private val userManager: UserManager,
+    @Background private val bgExecutor: Executor,
+    @Main private val mainExecutor: Executor,
+    private val notifLockscreenUserMgr: NotificationLockscreenUserManager
 ) : DataSource<PeopleHubModel> {
 
+    private var userChangeSubscription: Subscription? = null
     private val dataListeners = mutableListOf<DataListener<PeopleHubModel>>()
+    private val peopleHubManagerForUser = SparseArray<PeopleHubManager>()
 
     private val notificationEntryListener = object : NotificationEntryListener {
         override fun onEntryInflated(entry: NotificationEntry, inflatedFlags: Int) =
@@ -106,31 +108,56 @@
     }
 
     private fun removeVisibleEntry(entry: NotificationEntry) {
-        val key = extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey()
-        if (key?.let(peopleHubManager::removeActivePerson) == true) {
-            updateUi()
+        (extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey())?.let { key ->
+            val userId = entry.sbn.user.identifier
+            bgExecutor.execute {
+                val parentId = userManager.getProfileParent(userId)?.id ?: userId
+                mainExecutor.execute {
+                    if (peopleHubManagerForUser[parentId]?.removeActivePerson(key) == true) {
+                        updateUi()
+                    }
+                }
+            }
         }
     }
 
     private fun addVisibleEntry(entry: NotificationEntry) {
-        val personModel = extractor.extractPerson(entry.sbn) ?: entry.extractPerson()
-        if (personModel?.let(peopleHubManager::addActivePerson) == true) {
-            updateUi()
+        (extractor.extractPerson(entry.sbn) ?: entry.extractPerson())?.let { personModel ->
+            val userId = entry.sbn.user.identifier
+            bgExecutor.execute {
+                val parentId = userManager.getProfileParent(userId)?.id ?: userId
+                mainExecutor.execute {
+                    val manager = peopleHubManagerForUser[parentId]
+                            ?: PeopleHubManager().also { peopleHubManagerForUser.put(parentId, it) }
+                    if (manager.addActivePerson(personModel)) {
+                        updateUi()
+                    }
+                }
+            }
         }
     }
 
     override fun registerListener(listener: DataListener<PeopleHubModel>): Subscription {
-        val registerWithNotificationEntryManager = dataListeners.isEmpty()
+        val register = dataListeners.isEmpty()
         dataListeners.add(listener)
-        if (registerWithNotificationEntryManager) {
+        if (register) {
+            userChangeSubscription = notifLockscreenUserMgr.registerListener(
+                    object : NotificationLockscreenUserManager.UserChangedListener {
+                        override fun onUserChanged(userId: Int) = updateUi()
+                        override fun onCurrentProfilesChanged(
+                            currentProfiles: SparseArray<UserInfo>?
+                        ) = updateUi()
+                    })
             notificationEntryManager.addNotificationEntryListener(notificationEntryListener)
         } else {
-            listener.onDataChanged(peopleHubManager.getPeopleHubModel())
+            getPeopleHubModelForCurrentUser()?.let(listener::onDataChanged)
         }
         return object : Subscription {
             override fun unsubscribe() {
                 dataListeners.remove(listener)
                 if (dataListeners.isEmpty()) {
+                    userChangeSubscription?.unsubscribe()
+                    userChangeSubscription = null
                     notificationEntryManager
                             .removeNotificationEntryListener(notificationEntryListener)
                 }
@@ -138,16 +165,36 @@
         }
     }
 
+    private fun getPeopleHubModelForCurrentUser(): PeopleHubModel? {
+        val currentUserId = notifLockscreenUserMgr.currentUserId
+        val model = peopleHubManagerForUser[currentUserId]?.getPeopleHubModel()
+                ?: return null
+        val currentProfiles = notifLockscreenUserMgr.currentProfiles
+        return model.copy(people = model.people.filter { person ->
+            currentProfiles[person.userId]?.isQuietModeEnabled == false
+        })
+    }
+
     private fun updateUi() {
-        val model = peopleHubManager.getPeopleHubModel()
+        val model = getPeopleHubModelForCurrentUser() ?: return
         for (listener in dataListeners) {
             listener.onDataChanged(model)
         }
     }
 }
 
-@Singleton
-class PeopleHubManager @Inject constructor() {
+private fun NotificationLockscreenUserManager.registerListener(
+    listener: NotificationLockscreenUserManager.UserChangedListener
+): Subscription {
+    addUserChangedListener(listener)
+    return object : Subscription {
+        override fun unsubscribe() {
+            removeUserChangedListener(listener)
+        }
+    }
+}
+
+class PeopleHubManager {
 
     private val activePeople = mutableMapOf<PersonKey, PersonModel>()
     private val inactivePeople = ArrayDeque<PersonModel>(MAX_STORED_INACTIVE_PEOPLE)
@@ -157,7 +204,7 @@
             if (inactivePeople.size >= MAX_STORED_INACTIVE_PEOPLE) {
                 inactivePeople.removeLast()
             }
-            inactivePeople.push(data)
+            inactivePeople.add(data)
             return true
         }
         return false
@@ -190,63 +237,7 @@
             ?: extras.getString(Notification.EXTRA_TITLE)
             ?: return null
     val drawable = extractAvatarFromRow(this) ?: return null
-    val badgedAvatar = addBadgeToDrawable(drawable, row.context, sbn.packageName, sbn.user)
-    return PersonModel(key, name, badgedAvatar, clickIntent)
-}
-
-private fun addBadgeToDrawable(
-    drawable: Drawable,
-    context: Context,
-    packageName: String,
-    user: UserHandle
-): Drawable {
-    val pm = context.packageManager
-    val appInfo = pm.getApplicationInfoAsUser(packageName, 0, user)
-    return object : Drawable() {
-        override fun draw(canvas: Canvas) {
-            val iconBounds = getBounds()
-            val factory = object : BaseIconFactory(
-                    context,
-                    0 /* unused */,
-                    iconBounds.width(),
-                    true) {}
-            val badge = factory.createBadgedIconBitmap(
-                    appInfo.loadIcon(pm),
-                    user,
-                    true,
-                    appInfo.isInstantApp,
-                    null)
-            val badgeDrawable = BitmapDrawable(context.resources, badge.icon)
-                    .apply {
-                        alpha = drawable.alpha
-                        colorFilter = drawable.colorFilter
-                        val badgeWidth = TypedValue.applyDimension(
-                                TypedValue.COMPLEX_UNIT_DIP,
-                                15f,
-                                context.resources.displayMetrics
-                        ).toInt()
-                        setBounds(
-                                iconBounds.left + (iconBounds.width() - badgeWidth),
-                                iconBounds.top + (iconBounds.height() - badgeWidth),
-                                iconBounds.right,
-                                iconBounds.bottom)
-                    }
-            drawable.bounds = iconBounds
-            drawable.draw(canvas)
-            badgeDrawable.draw(canvas)
-        }
-
-        override fun setAlpha(alpha: Int) {
-            drawable.alpha = alpha
-        }
-
-        override fun setColorFilter(colorFilter: ColorFilter?) {
-            drawable.colorFilter = colorFilter
-        }
-
-        @PixelFormat.Opacity
-        override fun getOpacity(): Int = PixelFormat.OPAQUE
-    }
+    return PersonModel(key, name, drawable, clickIntent, sbn.user.identifier)
 }
 
 fun extractAvatarFromRow(entry: NotificationEntry): Drawable? =
@@ -272,4 +263,4 @@
         if (isMessagingNotification()) key else null
 
 private fun NotificationEntry.isMessagingNotification() =
-        sbn.notification.notificationStyle == Notification.MessagingStyle::class.java
\ No newline at end of file
+        sbn.notification.notificationStyle == Notification.MessagingStyle::class.java
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 3c247df..a8a35d0 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
@@ -65,7 +65,6 @@
 import android.widget.Chronometer;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.RemoteViews;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -150,7 +149,7 @@
     private StatusBarStateController mStatusbarStateController;
     private KeyguardBypassController mBypassController;
     private LayoutListener mLayoutListener;
-    private final NotificationContentInflater mNotificationInflater;
+    private NotificationRowContentBinder mNotificationContentBinder;
     private int mIconTransformContentShift;
     private int mIconTransformContentShiftNoIcon;
     private int mMaxHeadsUpHeightBeforeN;
@@ -464,7 +463,7 @@
      * Inflate views based off the inflation flags set. Inflation happens asynchronously.
      */
     public void inflateViews() {
-        mNotificationInflater.bindContent(mEntry, this, mInflationFlags, mBindParams,
+        mNotificationContentBinder.bindContent(mEntry, this, mInflationFlags, mBindParams,
                 false /* forceInflate */, mInflationCallback);
     }
 
@@ -478,7 +477,7 @@
         // View should not be reinflated in the future
         clearInflationFlags(inflationFlag);
         Runnable freeViewRunnable =
-                () -> mNotificationInflater.unbindContent(mEntry, this, inflationFlag);
+                () -> mNotificationContentBinder.unbindContent(mEntry, this, inflationFlag);
         switch (inflationFlag) {
             case FLAG_CONTENT_VIEW_HEADS_UP:
                 getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP,
@@ -742,23 +741,10 @@
         return mIsHeadsUp || mHeadsupDisappearRunning;
     }
 
-
-    public void setGroupManager(NotificationGroupManager groupManager) {
-        mGroupManager = groupManager;
-        mPrivateLayout.setGroupManager(groupManager);
-    }
-
     public void setRemoteInputController(RemoteInputController r) {
         mPrivateLayout.setRemoteInputController(r);
     }
 
-    public void setAppName(String appName) {
-        mAppName = appName;
-        if (mMenuRow != null && mMenuRow.getMenuView() != null) {
-            mMenuRow.setAppName(mAppName);
-        }
-    }
-
     public void addChildNotification(ExpandableNotificationRow row) {
         addChildNotification(row, -1);
     }
@@ -852,7 +838,7 @@
             mIsChildInGroup = isChildInGroup;
             if (mIsLowPriority) {
                 int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
-                mNotificationInflater.bindContent(mEntry, this, flags, mBindParams,
+                mNotificationContentBinder.bindContent(mEntry, this, flags, mBindParams,
                         false /* forceInflate */, mInflationCallback);
             }
         }
@@ -1105,10 +1091,6 @@
         return mPrivateLayout.getContractedNotificationHeader();
     }
 
-    public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
-        mOnExpandClickListener = onExpandClickListener;
-    }
-
     public void setLongPressListener(LongPressListener longPressListener) {
         mLongPressListener = longPressListener;
     }
@@ -1131,10 +1113,6 @@
         }
     }
 
-    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
-        mHeadsUpManager = headsUpManager;
-    }
-
     public HeadsUpManager getHeadsUpManager() {
         return mHeadsUpManager;
     }
@@ -1259,7 +1237,7 @@
             l.reInflateViews();
         }
         mEntry.getSbn().clearPackageContext();
-        mNotificationInflater.bindContent(mEntry, this, mInflationFlags, mBindParams,
+        mNotificationContentBinder.bindContent(mEntry, this, mInflationFlags, mBindParams,
                 true /* forceInflate */, mInflationCallback);
     }
 
@@ -1634,10 +1612,6 @@
         mBindParams.usesIncreasedHeadsUpHeight = use;
     }
 
-    public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
-        mNotificationInflater.setRemoteViewClickHandler(remoteViewClickHandler);
-    }
-
     /**
      * Set callback for notification content inflation
      *
@@ -1652,7 +1626,7 @@
             mNeedsRedaction = needsRedaction;
             if (needsRedaction) {
                 setInflationFlags(FLAG_CONTENT_VIEW_PUBLIC);
-                mNotificationInflater.bindContent(mEntry, this, FLAG_CONTENT_VIEW_PUBLIC,
+                mNotificationContentBinder.bindContent(mEntry, this, FLAG_CONTENT_VIEW_PUBLIC,
                         mBindParams, false /* forceInflate */, mInflationCallback);
             } else {
                 clearInflationFlags(FLAG_CONTENT_VIEW_PUBLIC);
@@ -1661,18 +1635,12 @@
         }
     }
 
-    @VisibleForTesting
-    public NotificationContentInflater getNotificationInflater() {
-        return mNotificationInflater;
-    }
-
     public interface ExpansionLogger {
         void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
 
     public ExpandableNotificationRow(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mNotificationInflater = new NotificationContentInflater();
         mMenuRow = new NotificationMenuRow(mContext);
         mImageResolver = new NotificationInlineImageResolver(context,
                 new NotificationInlineImageCache());
@@ -1680,8 +1648,30 @@
         initDimens();
     }
 
-    public void setBypassController(KeyguardBypassController bypassController) {
+    /**
+     * Initialize row.
+     */
+    public void initialize(
+            String appName,
+            String notificationKey,
+            ExpansionLogger logger,
+            KeyguardBypassController bypassController,
+            NotificationGroupManager groupManager,
+            HeadsUpManager headsUpManager,
+            NotificationRowContentBinder rowContentBinder,
+            OnExpandClickListener onExpandClickListener) {
+        mAppName = appName;
+        if (mMenuRow != null && mMenuRow.getMenuView() != null) {
+            mMenuRow.setAppName(mAppName);
+        }
+        mLogger = logger;
+        mLoggingKey = notificationKey;
         mBypassController = bypassController;
+        mGroupManager = groupManager;
+        mPrivateLayout.setGroupManager(groupManager);
+        mHeadsUpManager = headsUpManager;
+        mNotificationContentBinder = rowContentBinder;
+        mOnExpandClickListener = onExpandClickListener;
     }
 
     public void setStatusBarStateController(StatusBarStateController statusBarStateController) {
@@ -2920,11 +2910,6 @@
         return 0;
     }
 
-    public void setExpansionLogger(ExpansionLogger logger, String key) {
-        mLogger = logger;
-        mLoggingKey = key;
-    }
-
     public void onExpandedByGesture(boolean userExpanded) {
         int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
         if (mGroupManager.isSummaryOfGroup(mEntry.getSbn())) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCache.java
new file mode 100644
index 0000000..c11c60f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCache.java
@@ -0,0 +1,75 @@
+/*
+ * 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.widget.RemoteViews;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+
+/**
+ * Caches {@link RemoteViews} for a notification's content views.
+ */
+public interface NotifRemoteViewCache {
+
+    /**
+     * Whether the notification has the remote view cached
+     *
+     * @param entry notification
+     * @param flag inflation flag for content view
+     * @return true if the remote view is cached
+     */
+    boolean hasCachedView(NotificationEntry entry, @InflationFlag int flag);
+
+    /**
+     * Get the remote view for the content flag specified.
+     *
+     * @param entry notification
+     * @param flag inflation flag for the content view
+     * @return the remote view if it is cached, null otherwise
+     */
+    @Nullable RemoteViews getCachedView(NotificationEntry entry, @InflationFlag int flag);
+
+    /**
+     * Cache a remote view for a given content flag on a notification.
+     *
+     * @param entry notification
+     * @param flag inflation flag for the content view
+     * @param remoteView remote view to store
+     */
+    void putCachedView(
+            NotificationEntry entry,
+            @InflationFlag int flag,
+            RemoteViews remoteView);
+
+    /**
+     * Remove a cached remote view for a given content flag on a notification.
+     *
+     * @param entry notification
+     * @param flag inflation flag for the content view
+     */
+    void removeCachedView(NotificationEntry entry, @InflationFlag int flag);
+
+    /**
+     * Clear a notification's remote view cache.
+     *
+     * @param entry notification
+     */
+    void clearCache(NotificationEntry entry);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java
new file mode 100644
index 0000000..a19099a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java
@@ -0,0 +1,100 @@
+/*
+ * 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.util.ArrayMap;
+import android.util.SparseArray;
+import android.widget.RemoteViews;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+
+/**
+ * Implementation of remote view cache that keeps remote views cached for all active notifications.
+ */
+public class NotifRemoteViewCacheImpl implements NotifRemoteViewCache {
+    private final Map<NotificationEntry, SparseArray<RemoteViews>> mNotifCachedContentViews =
+            new ArrayMap<>();
+
+    @Inject
+    NotifRemoteViewCacheImpl(NotificationEntryManager entryManager) {
+        entryManager.addNotificationEntryListener(mEntryListener);
+    }
+
+    @Override
+    public boolean hasCachedView(NotificationEntry entry, @InflationFlag int flag) {
+        return getCachedView(entry, flag) != null;
+    }
+
+    @Override
+    public @Nullable RemoteViews getCachedView(NotificationEntry entry, @InflationFlag int flag) {
+        return getContentViews(entry).get(flag);
+    }
+
+    @Override
+    public void putCachedView(
+            NotificationEntry entry,
+            @InflationFlag int flag,
+            RemoteViews remoteView) {
+        getContentViews(entry).put(flag, remoteView);
+    }
+
+    @Override
+    public void removeCachedView(NotificationEntry entry, @InflationFlag int flag) {
+        getContentViews(entry).remove(flag);
+    }
+
+    @Override
+    public void clearCache(NotificationEntry entry) {
+        getContentViews(entry).clear();
+    }
+
+    private @NonNull SparseArray<RemoteViews> getContentViews(NotificationEntry entry) {
+        SparseArray<RemoteViews> contentViews = mNotifCachedContentViews.get(entry);
+        if (contentViews == null) {
+            throw new IllegalStateException(
+                    String.format("Remote view cache was never created for notification %s",
+                            entry.getKey()));
+        }
+        return contentViews;
+    }
+
+    private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
+        @Override
+        public void onPendingEntryAdded(NotificationEntry entry) {
+            mNotifCachedContentViews.put(entry, new SparseArray<>());
+        }
+
+        @Override
+        public void onEntryRemoved(
+                NotificationEntry entry,
+                @Nullable NotificationVisibility visibility,
+                boolean removedByUser) {
+            mNotifCachedContentViews.remove(entry);
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 172b72e..e1a6747 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
 
@@ -26,7 +27,6 @@
 import android.os.AsyncTask;
 import android.os.CancellationSignal;
 import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
 import android.util.Log;
 import android.view.View;
 import android.widget.RemoteViews;
@@ -35,6 +35,7 @@
 import com.android.internal.widget.ImageMessageConsumer;
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
@@ -49,17 +50,30 @@
 
 import java.util.HashMap;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * {@link NotificationContentInflater} binds content to a {@link ExpandableNotificationRow} by
  * asynchronously building the content's {@link RemoteViews} and applying it to the row.
  */
+@Singleton
+@VisibleForTesting(visibility = PACKAGE)
 public class NotificationContentInflater implements NotificationRowContentBinder {
 
     public static final String TAG = "NotifContentInflater";
 
-    private RemoteViews.OnClickHandler mRemoteViewClickHandler;
     private boolean mInflateSynchronously = false;
-    private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
+    private final NotificationRemoteInputManager mRemoteInputManager;
+    private final NotifRemoteViewCache mRemoteViewCache;
+
+    @Inject
+    public NotificationContentInflater(
+            NotifRemoteViewCache remoteViewCache,
+            NotificationRemoteInputManager remoteInputManager) {
+        mRemoteViewCache = remoteViewCache;
+        mRemoteInputManager = remoteInputManager;
+    }
 
     @Override
     public void bindContent(
@@ -76,27 +90,27 @@
             return;
         }
 
-        StatusBarNotification sbn = row.getEntry().getSbn();
+        StatusBarNotification sbn = entry.getSbn();
 
         // To check if the notification has inline image and preload inline image if necessary.
         row.getImageResolver().preloadImages(sbn.getNotification());
 
         if (forceInflate) {
-            mCachedContentViews.clear();
+            mRemoteViewCache.clearCache(entry);
         }
 
         AsyncInflationTask task = new AsyncInflationTask(
-                sbn,
                 mInflateSynchronously,
                 contentToBind,
-                mCachedContentViews,
+                mRemoteViewCache,
+                entry,
                 row,
                 bindParams.isLowPriority,
                 bindParams.isChildInGroup,
                 bindParams.usesIncreasedHeight,
                 bindParams.usesIncreasedHeadsUpHeight,
                 callback,
-                mRemoteViewClickHandler);
+                mRemoteInputManager.getRemoteViewsOnClickHandler());
         if (mInflateSynchronously) {
             task.onPostExecute(task.doInBackground());
         } else {
@@ -123,13 +137,15 @@
         result = inflateSmartReplyViews(result, reInflateFlags, entry,
                 row.getContext(), packageContext, row.getHeadsUpManager(),
                 row.getExistingSmartRepliesAndActions());
+
         apply(
                 inflateSynchronously,
                 result,
                 reInflateFlags,
-                mCachedContentViews,
+                mRemoteViewCache,
+                entry,
                 row,
-                mRemoteViewClickHandler,
+                mRemoteInputManager.getRemoteViewsOnClickHandler(),
                 null);
         return result;
     }
@@ -149,7 +165,7 @@
         int curFlag = 1;
         while (contentToUnbind != 0) {
             if ((contentToUnbind & curFlag) != 0) {
-                freeNotificationView(row, curFlag);
+                freeNotificationView(entry, row, curFlag);
             }
             contentToUnbind &= ~curFlag;
             curFlag = curFlag << 1;
@@ -157,34 +173,25 @@
     }
 
     /**
-     * Set click handler for notification remote views
-     *
-     * @param remoteViewClickHandler click handler for remote views
-     */
-    public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
-        mRemoteViewClickHandler = remoteViewClickHandler;
-    }
-
-    /**
      * Frees the content view associated with the inflation flag.  Will only succeed if the
      * view is safe to remove.
      *
      * @param inflateFlag the flag corresponding to the content view which should be freed
      */
-    private void freeNotificationView(ExpandableNotificationRow row,
+    private void freeNotificationView(NotificationEntry entry, ExpandableNotificationRow row,
             @InflationFlag int inflateFlag) {
         switch (inflateFlag) {
             case FLAG_CONTENT_VIEW_HEADS_UP:
                 if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
                     row.getPrivateLayout().setHeadsUpChild(null);
-                    mCachedContentViews.remove(FLAG_CONTENT_VIEW_HEADS_UP);
+                    mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP);
                     row.getPrivateLayout().setHeadsUpInflatedSmartReplies(null);
                 }
                 break;
             case FLAG_CONTENT_VIEW_PUBLIC:
                 if (row.getPublicLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) {
                     row.getPublicLayout().setContractedChild(null);
-                    mCachedContentViews.remove(FLAG_CONTENT_VIEW_PUBLIC);
+                    mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC);
                 }
                 break;
             case FLAG_CONTENT_VIEW_CONTRACTED:
@@ -245,11 +252,12 @@
         return result;
     }
 
-    public static CancellationSignal apply(
+    private static CancellationSignal apply(
             boolean inflateSynchronously,
             InflationProgress result,
             @InflationFlag int reInflateFlags,
-            ArrayMap<Integer, RemoteViews> cachedContentViews,
+            NotifRemoteViewCache remoteViewCache,
+            NotificationEntry entry,
             ExpandableNotificationRow row,
             RemoteViews.OnClickHandler remoteViewClickHandler,
             @Nullable InflationCallback callback) {
@@ -261,7 +269,7 @@
         if ((reInflateFlags & flag) != 0) {
             boolean isNewView =
                     !canReapplyRemoteView(result.newContentView,
-                            cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED));
+                            remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED));
             ApplyCallback applyCallback = new ApplyCallback() {
                 @Override
                 public void setResultView(View v) {
@@ -273,8 +281,8 @@
                     return result.newContentView;
                 }
             };
-            applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
-                    row, isNewView, remoteViewClickHandler, callback, privateLayout,
+            applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
+                    entry, row, isNewView, remoteViewClickHandler, callback, privateLayout,
                     privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
                             NotificationContentView.VISIBLE_TYPE_CONTRACTED),
                     runningInflations, applyCallback);
@@ -285,7 +293,7 @@
             if (result.newExpandedView != null) {
                 boolean isNewView =
                         !canReapplyRemoteView(result.newExpandedView,
-                                cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED));
+                                remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED));
                 ApplyCallback applyCallback = new ApplyCallback() {
                     @Override
                     public void setResultView(View v) {
@@ -297,8 +305,8 @@
                         return result.newExpandedView;
                     }
                 };
-                applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
-                        cachedContentViews, row, isNewView, remoteViewClickHandler,
+                applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
+                        entry, row, isNewView, remoteViewClickHandler,
                         callback, privateLayout, privateLayout.getExpandedChild(),
                         privateLayout.getVisibleWrapper(
                                 NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
@@ -311,7 +319,7 @@
             if (result.newHeadsUpView != null) {
                 boolean isNewView =
                         !canReapplyRemoteView(result.newHeadsUpView,
-                                cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP));
+                                remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP));
                 ApplyCallback applyCallback = new ApplyCallback() {
                     @Override
                     public void setResultView(View v) {
@@ -323,8 +331,8 @@
                         return result.newHeadsUpView;
                     }
                 };
-                applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
-                        cachedContentViews, row, isNewView, remoteViewClickHandler,
+                applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
+                        entry, row, isNewView, remoteViewClickHandler,
                         callback, privateLayout, privateLayout.getHeadsUpChild(),
                         privateLayout.getVisibleWrapper(
                                 VISIBLE_TYPE_HEADSUP), runningInflations,
@@ -336,7 +344,7 @@
         if ((reInflateFlags & flag) != 0) {
             boolean isNewView =
                     !canReapplyRemoteView(result.newPublicView,
-                            cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC));
+                            remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC));
             ApplyCallback applyCallback = new ApplyCallback() {
                 @Override
                 public void setResultView(View v) {
@@ -348,15 +356,16 @@
                     return result.newPublicView;
                 }
             };
-            applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
-                    row, isNewView, remoteViewClickHandler, callback,
+            applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
+                    entry, row, isNewView, remoteViewClickHandler, callback,
                     publicLayout, publicLayout.getContractedChild(),
                     publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
                     runningInflations, applyCallback);
         }
 
         // Let's try to finish, maybe nobody is even inflating anything
-        finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, callback, row);
+        finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations, callback, entry,
+                row);
         CancellationSignal cancellationSignal = new CancellationSignal();
         cancellationSignal.setOnCancelListener(
                 () -> runningInflations.values().forEach(CancellationSignal::cancel));
@@ -369,7 +378,8 @@
             final InflationProgress result,
             final @InflationFlag int reInflateFlags,
             @InflationFlag int inflationId,
-            final ArrayMap<Integer, RemoteViews> cachedContentViews,
+            final NotifRemoteViewCache remoteViewCache,
+            final NotificationEntry entry,
             final ExpandableNotificationRow row,
             boolean isNewView,
             RemoteViews.OnClickHandler remoteViewClickHandler,
@@ -397,7 +407,7 @@
                     existingWrapper.onReinflated();
                 }
             } catch (Exception e) {
-                handleInflationError(runningInflations, e, row.getEntry().getSbn(), callback);
+                handleInflationError(runningInflations, e, row.getEntry(), callback);
                 // Add a running inflation to make sure we don't trigger callbacks.
                 // Safe to do because only happens in tests.
                 runningInflations.put(inflationId, new CancellationSignal());
@@ -422,8 +432,8 @@
                     existingWrapper.onReinflated();
                 }
                 runningInflations.remove(inflationId);
-                finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations,
-                        callback, row);
+                finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations,
+                        callback, entry, row);
             }
 
             @Override
@@ -448,7 +458,7 @@
                     onViewApplied(newView);
                 } catch (Exception anotherException) {
                     runningInflations.remove(inflationId);
-                    handleInflationError(runningInflations, e, row.getEntry().getSbn(),
+                    handleInflationError(runningInflations, e, row.getEntry(),
                             callback);
                 }
             }
@@ -474,7 +484,7 @@
 
     private static void handleInflationError(
             HashMap<Integer, CancellationSignal> runningInflations, Exception e,
-            StatusBarNotification notification, @Nullable InflationCallback callback) {
+            NotificationEntry notification, @Nullable InflationCallback callback) {
         Assert.isMainThread();
         runningInflations.values().forEach(CancellationSignal::cancel);
         if (callback != null) {
@@ -488,11 +498,11 @@
      * @return true if the inflation was finished
      */
     private static boolean finishIfDone(InflationProgress result,
-            @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
+            @InflationFlag int reInflateFlags, NotifRemoteViewCache remoteViewCache,
             HashMap<Integer, CancellationSignal> runningInflations,
-            @Nullable InflationCallback endListener, ExpandableNotificationRow row) {
+            @Nullable InflationCallback endListener, NotificationEntry entry,
+            ExpandableNotificationRow row) {
         Assert.isMainThread();
-        NotificationEntry entry = row.getEntry();
         NotificationContentView privateLayout = row.getPrivateLayout();
         NotificationContentView publicLayout = row.getPublicLayout();
         if (runningInflations.isEmpty()) {
@@ -500,23 +510,27 @@
                 if (result.inflatedContentView != null) {
                     // New view case
                     privateLayout.setContractedChild(result.inflatedContentView);
-                    cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
-                } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED) != null) {
+                    remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED,
+                            result.newContentView);
+                } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)) {
                     // Reinflation case. Only update if it's still cached (i.e. view has not been
                     // freed while inflating).
-                    cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
+                    remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED,
+                            result.newContentView);
                 }
             }
 
             if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
                 if (result.inflatedExpandedView != null) {
                     privateLayout.setExpandedChild(result.inflatedExpandedView);
-                    cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
+                    remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED,
+                            result.newExpandedView);
                 } else if (result.newExpandedView == null) {
                     privateLayout.setExpandedChild(null);
-                    cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, null);
-                } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED) != null) {
-                    cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
+                    remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED);
+                } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)) {
+                    remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED,
+                            result.newExpandedView);
                 }
                 if (result.newExpandedView != null) {
                     privateLayout.setExpandedInflatedSmartReplies(
@@ -530,12 +544,14 @@
             if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
                 if (result.inflatedHeadsUpView != null) {
                     privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
-                    cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
+                    remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP,
+                            result.newHeadsUpView);
                 } else if (result.newHeadsUpView == null) {
                     privateLayout.setHeadsUpChild(null);
-                    cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, null);
-                } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP) != null) {
-                    cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
+                    remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP);
+                } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)) {
+                    remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP,
+                            result.newHeadsUpView);
                 }
                 if (result.newHeadsUpView != null) {
                     privateLayout.setHeadsUpInflatedSmartReplies(
@@ -548,16 +564,18 @@
             if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
                 if (result.inflatedPublicView != null) {
                     publicLayout.setContractedChild(result.inflatedPublicView);
-                    cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
-                } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC) != null) {
-                    cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
+                    remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC,
+                            result.newPublicView);
+                } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)) {
+                    remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC,
+                            result.newPublicView);
                 }
             }
 
             entry.headsUpStatusBarText = result.headsUpStatusBarText;
             entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
             if (endListener != null) {
-                endListener.onAsyncInflationFinished(row.getEntry(), reInflateFlags);
+                endListener.onAsyncInflationFinished(entry, reInflateFlags);
             }
             return true;
         }
@@ -615,7 +633,7 @@
     public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
             implements InflationCallback, InflationTask {
 
-        private final StatusBarNotification mSbn;
+        private final NotificationEntry mEntry;
         private final Context mContext;
         private final boolean mInflateSynchronously;
         private final boolean mIsLowPriority;
@@ -624,17 +642,17 @@
         private final InflationCallback mCallback;
         private final boolean mUsesIncreasedHeadsUpHeight;
         private @InflationFlag int mReInflateFlags;
-        private final ArrayMap<Integer, RemoteViews> mCachedContentViews;
+        private final NotifRemoteViewCache mRemoteViewCache;
         private ExpandableNotificationRow mRow;
         private Exception mError;
         private RemoteViews.OnClickHandler mRemoteViewClickHandler;
         private CancellationSignal mCancellationSignal;
 
         private AsyncInflationTask(
-                StatusBarNotification notification,
                 boolean inflateSynchronously,
                 @InflationFlag int reInflateFlags,
-                ArrayMap<Integer, RemoteViews> cachedContentViews,
+                NotifRemoteViewCache cache,
+                NotificationEntry entry,
                 ExpandableNotificationRow row,
                 boolean isLowPriority,
                 boolean isChildInGroup,
@@ -642,11 +660,11 @@
                 boolean usesIncreasedHeadsUpHeight,
                 InflationCallback callback,
                 RemoteViews.OnClickHandler remoteViewClickHandler) {
+            mEntry = entry;
             mRow = row;
-            mSbn = notification;
             mInflateSynchronously = inflateSynchronously;
             mReInflateFlags = reInflateFlags;
-            mCachedContentViews = cachedContentViews;
+            mRemoteViewCache = cache;
             mContext = mRow.getContext();
             mIsLowPriority = isLowPriority;
             mIsChildInGroup = isChildInGroup;
@@ -654,7 +672,6 @@
             mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight;
             mRemoteViewClickHandler = remoteViewClickHandler;
             mCallback = callback;
-            NotificationEntry entry = row.getEntry();
             entry.setInflationTask(this);
         }
 
@@ -667,12 +684,13 @@
         @Override
         protected InflationProgress doInBackground(Void... params) {
             try {
+                final StatusBarNotification sbn = mEntry.getSbn();
                 final Notification.Builder recoveredBuilder
                         = Notification.Builder.recoverBuilder(mContext,
-                        mSbn.getNotification());
+                        sbn.getNotification());
 
-                Context packageContext = mSbn.getPackageContext(mContext);
-                Notification notification = mSbn.getNotification();
+                Context packageContext = sbn.getPackageContext(mContext);
+                Notification notification = sbn.getNotification();
                 if (notification.isMediaNotification()) {
                     MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
                             packageContext);
@@ -681,7 +699,7 @@
                 InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
                         recoveredBuilder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
                         mUsesIncreasedHeadsUpHeight, packageContext);
-                return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(),
+                return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry,
                         mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
                         mRow.getExistingSmartRepliesAndActions());
             } catch (Exception e) {
@@ -694,20 +712,20 @@
         protected void onPostExecute(InflationProgress result) {
             if (mError == null) {
                 mCancellationSignal = apply(mInflateSynchronously, result, mReInflateFlags,
-                        mCachedContentViews, mRow, mRemoteViewClickHandler, this);
+                        mRemoteViewCache, mEntry, mRow, mRemoteViewClickHandler, this);
             } else {
                 handleError(mError);
             }
         }
 
         private void handleError(Exception e) {
-            mRow.getEntry().onInflationTaskFinished();
-            StatusBarNotification sbn = mRow.getEntry().getSbn();
+            mEntry.onInflationTaskFinished();
+            StatusBarNotification sbn = mEntry.getSbn();
             final String ident = sbn.getPackageName() + "/0x"
                     + Integer.toHexString(sbn.getId());
             Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
             if (mCallback != null) {
-                mCallback.handleInflationException(sbn,
+                mCallback.handleInflationException(mRow.getEntry(),
                         new InflationException("Couldn't inflate contentViews" + e));
             }
         }
@@ -729,17 +747,17 @@
         }
 
         @Override
-        public void handleInflationException(StatusBarNotification notification, Exception e) {
+        public void handleInflationException(NotificationEntry entry, Exception e) {
             handleError(e);
         }
 
         @Override
         public void onAsyncInflationFinished(NotificationEntry entry,
                 @InflationFlag int inflatedFlags) {
-            mRow.getEntry().onInflationTaskFinished();
+            mEntry.onInflationTaskFinished();
             mRow.onNotificationUpdated();
             if (mCallback != null) {
-                mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags);
+                mCallback.onAsyncInflationFinished(mEntry, inflatedFlags);
             }
 
             // Notify the resolver that the inflation task has finished,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
index 2fe54c0..9b95bff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.service.notification.StatusBarNotification;
 
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
@@ -138,10 +137,10 @@
         /**
          * Callback for when there is an inflation exception
          *
-         * @param notification notification which failed to inflate content
+         * @param entry notification which failed to inflate content
          * @param e exception
          */
-        void handleInflationException(StatusBarNotification notification, Exception e);
+        void handleInflationException(NotificationEntry entry, Exception e);
 
         /**
          * Callback for after the content views finish inflating.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
new file mode 100644
index 0000000..df8653c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -0,0 +1,44 @@
+/*
+ * 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 javax.inject.Singleton;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Dagger Module containing notification row and view inflation implementations.
+ */
+@Module
+public abstract class NotificationRowModule {
+    /**
+     * Provides notification row content binder instance.
+     */
+    @Binds
+    @Singleton
+    public abstract NotificationRowContentBinder provideNotificationRowContentBinder(
+            NotificationContentInflater contentBinderImpl);
+
+    /**
+     * Provides notification remote view cache instance.
+     */
+    @Binds
+    @Singleton
+    public abstract NotifRemoteViewCache provideNotifRemoteViewCache(
+            NotifRemoteViewCacheImpl cacheImpl);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index 2761689..09c1fad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -187,18 +187,18 @@
     @Override
     public boolean beginsSection(@NonNull View view, @Nullable View previous) {
         boolean begin = false;
-        if (view instanceof ExpandableNotificationRow) {
-            if (previous instanceof ExpandableNotificationRow) {
+        if (view instanceof ActivatableNotificationView) {
+            if (previous instanceof ActivatableNotificationView) {
                 // If we're drawing the first non-person notification, break out a section
-                ExpandableNotificationRow curr = (ExpandableNotificationRow) view;
-                ExpandableNotificationRow prev = (ExpandableNotificationRow) previous;
+                ActivatableNotificationView curr = (ActivatableNotificationView) view;
+                ActivatableNotificationView prev = (ActivatableNotificationView) previous;
 
-                begin =  curr.getEntry().getBucket() != prev.getEntry().getBucket();
+                begin = getBucket(curr) != getBucket(prev);
             }
         }
 
         if (!begin) {
-            begin = view == mGentleHeader || previous == mPeopleHubView;
+            begin = view == mGentleHeader || view == mPeopleHubView;
         }
 
         return begin;
@@ -230,29 +230,42 @@
             return;
         }
 
-        int lastPersonIndex = -1;
-        int firstGentleNotifIndex = -1;
+        boolean peopleNotificationsPresent = false;
+        int firstNonHeadsUpIndex = -1;
+        int firstGentleIndex = -1;
+        int notifCount = 0;
 
         final int n = mParent.getChildCount();
         for (int i = 0; i < n; i++) {
             View child = mParent.getChildAt(i);
-            if (child instanceof ExpandableNotificationRow
-                    && child.getVisibility() != View.GONE) {
+            if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) {
+                notifCount++;
                 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                if (firstNonHeadsUpIndex == -1 && !row.isHeadsUp()) {
+                    firstNonHeadsUpIndex = i;
+                }
                 if (row.getEntry().getBucket() == BUCKET_PEOPLE) {
-                    lastPersonIndex = i;
+                    peopleNotificationsPresent = true;
                 }
                 if (row.getEntry().getBucket() == BUCKET_SILENT) {
-                    firstGentleNotifIndex = i;
+                    firstGentleIndex = i;
                     break;
                 }
             }
         }
 
-        // make room for peopleHub
-        firstGentleNotifIndex += adjustPeopleHubVisibilityAndPosition(lastPersonIndex);
+        if (firstNonHeadsUpIndex == -1) {
+            firstNonHeadsUpIndex = firstGentleIndex != -1 ? firstGentleIndex : notifCount;
+        }
 
-        adjustGentleHeaderVisibilityAndPosition(firstGentleNotifIndex);
+        // make room for peopleHub
+        int offset = adjustPeopleHubVisibilityAndPosition(
+                firstNonHeadsUpIndex, peopleNotificationsPresent);
+        if (firstGentleIndex != -1) {
+            firstGentleIndex += offset;
+        }
+
+        adjustGentleHeaderVisibilityAndPosition(firstGentleIndex);
 
         mGentleHeader.setAreThereDismissableGentleNotifs(
                 mParent.hasActiveClearableNotifications(ROWS_GENTLE));
@@ -294,13 +307,15 @@
         }
     }
 
-    private int adjustPeopleHubVisibilityAndPosition(int lastPersonIndex) {
-        final boolean showPeopleHeader = mPeopleHubVisible
-                && mNumberOfSections > 2
-                && mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
+    private int adjustPeopleHubVisibilityAndPosition(
+            int targetIndex, boolean peopleNotificationsPresent) {
+        final boolean showPeopleHeader = mNumberOfSections > 2
+                && mStatusBarStateController.getState() != StatusBarState.KEYGUARD
+                && (peopleNotificationsPresent || mPeopleHubVisible);
         final int currentHubIndex = mParent.indexOfChild(mPeopleHubView);
         final boolean currentlyVisible = currentHubIndex >= 0;
-        int targetIndex = lastPersonIndex + 1;
+
+        mPeopleHubView.setCanSwipe(showPeopleHeader && !peopleNotificationsPresent);
 
         if (!showPeopleHeader) {
             if (currentlyVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 6d0fcc3..4845ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -31,6 +31,7 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 
 class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeActionHelper {
@@ -298,8 +299,8 @@
     @Override
     public Animator getViewTranslationAnimator(View v, float target,
             ValueAnimator.AnimatorUpdateListener listener) {
-        if (v instanceof SwipeableView) {
-            return ((SwipeableView) v).getTranslateViewAnimator(target, listener);
+        if (v instanceof ExpandableNotificationRow) {
+            return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
         } else {
             return superGetViewTranslationAnimator(v, target, listener);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
index a079606..e5717ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
@@ -16,38 +16,22 @@
 
 package com.android.systemui.statusbar.notification.stack
 
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
-import android.animation.ObjectAnimator
-import android.animation.ValueAnimator
 import android.content.Context
 import android.util.AttributeSet
-import android.util.FloatProperty
 import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
-import android.widget.LinearLayout
-import android.widget.TextView
 import com.android.systemui.R
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin
 import com.android.systemui.statusbar.notification.people.DataListener
 import com.android.systemui.statusbar.notification.people.PersonViewModel
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView
 
-private val TRANSLATE_CONTENT = object : FloatProperty<PeopleHubView>("translate") {
-    override fun setValue(view: PeopleHubView, value: Float) {
-        view.translation = value
-    }
-
-    override fun get(view: PeopleHubView) = view.translation
-}
-
 class PeopleHubView(context: Context, attrs: AttributeSet) :
         ActivatableNotificationView(context, attrs), SwipeableView {
 
     private lateinit var contents: ViewGroup
     private lateinit var personControllers: List<PersonDataListenerImpl>
-    private var translateAnim: ObjectAnimator? = null
 
     val personViewAdapters: Sequence<DataListener<PersonViewModel?>>
         get() = personControllers.asSequence()
@@ -56,9 +40,10 @@
         super.onFinishInflate()
         contents = requireViewById(R.id.people_list)
         personControllers = (0 until contents.childCount)
+                .reversed()
                 .asSequence()
                 .mapNotNull { idx ->
-                    (contents.getChildAt(idx) as? LinearLayout)?.let(::PersonDataListenerImpl)
+                    (contents.getChildAt(idx) as? ImageView)?.let(::PersonDataListenerImpl)
                 }
                 .toList()
     }
@@ -69,41 +54,32 @@
 
     override fun createMenu(): NotificationMenuRowPlugin? = null
 
-    override fun getTranslateViewAnimator(
-        leftTarget: Float,
-        listener: ValueAnimator.AnimatorUpdateListener?
-    ): Animator =
-            ObjectAnimator
-                    .ofFloat(this, TRANSLATE_CONTENT, leftTarget)
-                    .apply {
-                        listener?.let { addUpdateListener(listener) }
-                        addListener(object : AnimatorListenerAdapter() {
-                            override fun onAnimationEnd(anim: Animator) {
-                                translateAnim = null
-                            }
-                        })
-                    }
-                    .also {
-                        translateAnim?.cancel()
-                        translateAnim = it
-                    }
-
     override fun resetTranslation() {
-        translateAnim?.cancel()
         translationX = 0f
     }
 
-    private inner class PersonDataListenerImpl(val viewGroup: ViewGroup) :
+    override fun setTranslation(translation: Float) {
+        if (canSwipe) {
+            super.setTranslation(translation)
+        }
+    }
+
+    var canSwipe: Boolean = true
+        set(value) {
+            if (field != value) {
+                if (field) {
+                    resetTranslation()
+                }
+                field = value
+            }
+        }
+
+    private inner class PersonDataListenerImpl(val avatarView: ImageView) :
             DataListener<PersonViewModel?> {
 
-        val nameView = viewGroup.requireViewById<TextView>(R.id.person_name)
-        val avatarView = viewGroup.requireViewById<ImageView>(R.id.person_icon)
-
         override fun onDataChanged(data: PersonViewModel?) {
-            viewGroup.visibility = data?.let { View.VISIBLE } ?: View.INVISIBLE
-            nameView.text = data?.name
             avatarView.setImageDrawable(data?.icon)
-            viewGroup.setOnClickListener { data?.onClick?.invoke() }
+            avatarView.setOnClickListener { data?.onClick?.invoke() }
         }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SwipeableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SwipeableView.java
index 6c6ef61..49e59a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SwipeableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SwipeableView.java
@@ -16,9 +16,6 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-
 import androidx.annotation.Nullable;
 
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -32,10 +29,6 @@
     /** Optionally creates a menu for this view. */
     @Nullable NotificationMenuRowPlugin createMenu();
 
-    /** Animator for translating the view, simulating a swipe. */
-    Animator getTranslateViewAnimator(
-            float leftTarget, ValueAnimator.AnimatorUpdateListener listener);
-
     /** Sets the translation amount for an in-progress swipe. */
     void setTranslation(float translate);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index f7d52b5..ad1aa83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -98,7 +98,12 @@
                 bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0
             }
         }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD)
-        lockscreenUserManager.addUserChangedListener { pendingUnlockType = null }
+        lockscreenUserManager.addUserChangedListener(
+                object : NotificationLockscreenUserManager.UserChangedListener {
+                    override fun onUserChanged(userId: Int) {
+                        pendingUnlockType = null
+                    }
+                })
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index fe0739f..896b6e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -32,7 +32,6 @@
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.AsyncInflationTask;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
@@ -428,7 +427,7 @@
          * The notification is still pending inflation but we've decided that we no longer need
          * the content view (e.g. suppression might have changed and we decided we need to transfer
          * back). However, there is no way to abort just this inflation if other inflation requests
-         * have started (see {@link AsyncInflationTask#supersedeTask(InflationTask)}). So instead
+         * have started (see {@link InflationTask#supersedeTask(InflationTask)}). So instead
          * we just flag it as aborted and free when it's inflated.
          */
         boolean mAbortOnInflation;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 9dc8fbf..0f3af09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -24,8 +24,6 @@
 import android.graphics.PorterDuffXfermode;
 import android.util.AttributeSet;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 public class NotificationPanelView extends PanelView {
 
     private static final boolean DEBUG = false;
@@ -38,14 +36,10 @@
     static final String COUNTER_PANEL_OPEN = "panel_open";
     static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
 
-    @VisibleForTesting
-    protected KeyguardAffordanceHelper mAffordanceHelper;
-
-    private int mOldLayoutDirection;
-
     private int mCurrentPanelAlpha;
     private final Paint mAlphaPaint = new Paint();
     private boolean mDozing;
+    private RtlChangeListener mRtlChangeListener;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -57,9 +51,8 @@
 
     @Override
     public void onRtlPropertiesChanged(int layoutDirection) {
-        if (layoutDirection != mOldLayoutDirection) {
-            mAffordanceHelper.onRtlPropertiesChanged();
-            mOldLayoutDirection = layoutDirection;
+        if (mRtlChangeListener != null) {
+            mRtlChangeListener.onRtlPropertielsChanged(layoutDirection);
         }
     }
 
@@ -95,4 +88,11 @@
         return !mDozing;
     }
 
+    void setRtlChangeListener(RtlChangeListener listener) {
+        mRtlChangeListener = listener;
+    }
+
+    interface RtlChangeListener {
+        void onRtlPropertielsChanged(int layoutDirection);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index bbde25b..90ec2a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -449,6 +449,7 @@
     private PluginManager mPluginManager;
     private FrameLayout mPluginFrame;
     private NPVPluginManager mNPVPluginManager;
+    private int mOldLayoutDirection;
 
     @Inject
     public NotificationPanelViewController(NotificationPanelView view,
@@ -603,6 +604,13 @@
 
             }
         }, HomeControlsPlugin.class, false);
+
+        mView.setRtlChangeListener(layoutDirection -> {
+            if (layoutDirection != mOldLayoutDirection) {
+                mAffordanceHelper.onRtlPropertiesChanged();
+                mOldLayoutDirection = layoutDirection;
+            }
+        });
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 277a761..30825ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -205,6 +205,7 @@
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -413,6 +414,7 @@
     private final NotificationGutsManager mGutsManager;
     private final NotificationLogger mNotificationLogger;
     private final NotificationEntryManager mEntryManager;
+    private final NotificationRowContentBinder mRowContentBinder;
     private NotificationListController mNotificationListController;
     private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
     private final NotificationViewHierarchyManager mViewHierarchyManager;
@@ -634,6 +636,7 @@
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
             NotificationEntryManager notificationEntryManager,
+            NotificationRowContentBinder notificationRowContentBinder,
             NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             NotificationViewHierarchyManager notificationViewHierarchyManager,
             KeyguardViewMediator keyguardViewMediator,
@@ -715,6 +718,7 @@
         mGutsManager = notificationGutsManager;
         mNotificationLogger = notificationLogger;
         mEntryManager = notificationEntryManager;
+        mRowContentBinder = notificationRowContentBinder;
         mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
         mViewHierarchyManager = notificationViewHierarchyManager;
         mKeyguardViewMediator = keyguardViewMediator;
@@ -1240,6 +1244,7 @@
         final NotificationRowBinderImpl rowBinder =
                 new NotificationRowBinderImpl(
                         mContext,
+                        mRowContentBinder,
                         mAllowNotificationLongPress,
                         mKeyguardBypassController,
                         mStatusBarStateController,
@@ -1272,7 +1277,11 @@
 
         mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
 
-        mEntryManager.setRowBinder(rowBinder);
+        if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+            mEntryManager.setRowBinder(rowBinder);
+            rowBinder.setInflationCallback(mEntryManager);
+        }
+
         mRemoteInputUriController.attach(mEntryManager);
 
         rowBinder.setNotificationClicker(new NotificationClicker(
@@ -1282,7 +1291,7 @@
         mNotificationListController.bind();
 
         if (mFeatureFlags.isNewNotifPipelineEnabled()) {
-            mNewNotifPipeline.get().initialize(mNotificationListener);
+            mNewNotifPipeline.get().initialize(mNotificationListener, rowBinder);
         }
         mEntryManager.attach(mNotificationListener);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
index 153ca22..df74107 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -69,6 +69,7 @@
 import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -125,6 +126,7 @@
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
             NotificationEntryManager notificationEntryManager,
+            NotificationRowContentBinder notificationRowContentBinder,
             NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             NotificationViewHierarchyManager notificationViewHierarchyManager,
             KeyguardViewMediator keyguardViewMediator,
@@ -207,6 +209,7 @@
                 notificationGutsManager,
                 notificationLogger,
                 notificationEntryManager,
+                notificationRowContentBinder,
                 notificationInterruptionStateProvider,
                 notificationViewHierarchyManager,
                 keyguardViewMediator,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 0fd0dab..12a6516 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -217,7 +217,7 @@
             mEntryManager.addNotificationLifetimeExtenders(
                     remoteInputManager.getLifetimeExtenders());
             notificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
-                    mEntryManager, this);
+                    this);
             mNotificationInterruptionStateProvider.setUpWithPresenter(
                     this, mHeadsUpManager, this::canHeadsUp);
             mLockscreenUserManager.setUpWithPresenter(this);
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
new file mode 100644
index 0000000..d413308
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -0,0 +1,333 @@
+/*
+ * 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.wm;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.IDisplayWindowInsetsController;
+import android.view.InsetsSource;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.WindowInsets;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+import com.android.systemui.dagger.qualifiers.Main;
+
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Manages IME control at the display-level. This occurs when IME comes up in multi-window mode.
+ */
+@Singleton
+public class DisplayImeController implements DisplayWindowController.DisplayWindowListener {
+    private static final String TAG = "DisplayImeController";
+
+    static final int ANIMATION_DURATION_SHOW_MS = 275;
+    static final int ANIMATION_DURATION_HIDE_MS = 340;
+    static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+    private static final int DIRECTION_NONE = 0;
+    private static final int DIRECTION_SHOW = 1;
+    private static final int DIRECTION_HIDE = 2;
+
+    SystemWindows mSystemWindows;
+    final Handler mHandler;
+
+    final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
+
+    final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
+
+    @Inject
+    DisplayImeController(SystemWindows syswin, DisplayWindowController displayController,
+            @Main Handler mainHandler) {
+        mHandler = mainHandler;
+        mSystemWindows = syswin;
+        displayController.addDisplayWindowListener(this);
+    }
+
+    @Override
+    public void onDisplayAdded(int displayId) {
+        // Add's a system-ui window-manager specifically for ime. This type is special because
+        // WM will defer IME inset handling to it in multi-window scenarious.
+        PerDisplay pd = new PerDisplay(displayId,
+                mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation());
+        try {
+            mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Unable to set insets controller on display " + displayId);
+        }
+        mImePerDisplay.put(displayId, pd);
+    }
+
+    @Override
+    public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+        PerDisplay pd = mImePerDisplay.get(displayId);
+        if (pd == null) {
+            return;
+        }
+        if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()
+                != pd.mRotation && isImeShowing(displayId)) {
+            pd.startAnimation(true);
+        }
+    }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        try {
+            mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
+        }
+        mImePerDisplay.remove(displayId);
+    }
+
+    private boolean isImeShowing(int displayId) {
+        PerDisplay pd = mImePerDisplay.get(displayId);
+        if (pd == null) {
+            return false;
+        }
+        final InsetsSource imeSource = pd.mInsetsState.getSource(InsetsState.ITYPE_IME);
+        return imeSource != null && pd.mImeSourceControl != null && imeSource.isVisible();
+    }
+
+    private void dispatchPositionChanged(int displayId, int imeTop,
+            SurfaceControl.Transaction t) {
+        synchronized (mPositionProcessors) {
+            for (ImePositionProcessor pp : mPositionProcessors) {
+                pp.onImePositionChanged(displayId, imeTop, t);
+            }
+        }
+    }
+
+    private void dispatchStartPositioning(int displayId, int imeTop, int finalImeTop,
+            boolean show, SurfaceControl.Transaction t) {
+        synchronized (mPositionProcessors) {
+            for (ImePositionProcessor pp : mPositionProcessors) {
+                pp.onImeStartPositioning(displayId, imeTop, finalImeTop, show, t);
+            }
+        }
+    }
+
+    private void dispatchEndPositioning(int displayId, int imeTop, boolean show,
+            SurfaceControl.Transaction t) {
+        synchronized (mPositionProcessors) {
+            for (ImePositionProcessor pp : mPositionProcessors) {
+                pp.onImeEndPositioning(displayId, imeTop, show, t);
+            }
+        }
+    }
+
+    /**
+     * Adds an {@link ImePositionProcessor} to be called during ime position updates.
+     */
+    public void addPositionProcessor(ImePositionProcessor processor) {
+        synchronized (mPositionProcessors) {
+            if (mPositionProcessors.contains(processor)) {
+                return;
+            }
+            mPositionProcessors.add(processor);
+        }
+    }
+
+    /**
+     * Removes an {@link ImePositionProcessor} to be called during ime position updates.
+     */
+    public void removePositionProcessor(ImePositionProcessor processor) {
+        synchronized (mPositionProcessors) {
+            mPositionProcessors.remove(processor);
+        }
+    }
+
+    class PerDisplay extends IDisplayWindowInsetsController.Stub {
+        final int mDisplayId;
+        final InsetsState mInsetsState = new InsetsState();
+        InsetsSourceControl mImeSourceControl = null;
+        int mAnimationDirection = DIRECTION_NONE;
+        ValueAnimator mAnimation = null;
+        int mRotation = Surface.ROTATION_0;
+
+        PerDisplay(int displayId, int initialRotation) {
+            mDisplayId = displayId;
+            mRotation = initialRotation;
+        }
+
+        @Override
+        public void insetsChanged(InsetsState insetsState) {
+            if (mInsetsState.equals(insetsState)) {
+                return;
+            }
+            mInsetsState.set(insetsState, true /* copySources */);
+        }
+
+        @Override
+        public void insetsControlChanged(InsetsState insetsState,
+                InsetsSourceControl[] activeControls) {
+            insetsChanged(insetsState);
+            if (activeControls != null) {
+                for (InsetsSourceControl activeControl : activeControls) {
+                    if (activeControl == null) {
+                        continue;
+                    }
+                    if (activeControl.getType() == InsetsState.ITYPE_IME) {
+                        mImeSourceControl = activeControl;
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void showInsets(int types, boolean fromIme) {
+            if ((types & WindowInsets.Type.ime()) == 0) {
+                return;
+            }
+            startAnimation(true /* show */);
+        }
+
+        @Override
+        public void hideInsets(int types, boolean fromIme) {
+            if ((types & WindowInsets.Type.ime()) == 0) {
+                return;
+            }
+            startAnimation(false /* show */);
+        }
+
+        /**
+         * Sends the local visibility state back to window manager. Needed for legacy adjustForIme.
+         */
+        private void setVisibleDirectly(boolean visible) {
+            mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible);
+            try {
+                mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+            } catch (RemoteException e) {
+            }
+        }
+
+        private int imeTop(InsetsSource imeSource, float surfaceOffset) {
+            return imeSource.getFrame().top + (int) surfaceOffset;
+        }
+
+        private void startAnimation(final boolean show) {
+            final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME);
+            if (imeSource == null || mImeSourceControl == null) {
+                return;
+            }
+            if ((mAnimationDirection == DIRECTION_SHOW && show)
+                    || (mAnimationDirection == DIRECTION_HIDE && !show)) {
+                return;
+            }
+            if (mAnimationDirection != DIRECTION_NONE) {
+                mAnimation.end();
+                mAnimationDirection = DIRECTION_NONE;
+            }
+            mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
+            mHandler.post(() -> {
+                final float defaultY = mImeSourceControl.getSurfacePosition().y;
+                final float x = mImeSourceControl.getSurfacePosition().x;
+                final float startY = show ? defaultY + imeSource.getFrame().height() : defaultY;
+                final float endY = show ? defaultY : defaultY + imeSource.getFrame().height();
+                mAnimation = ValueAnimator.ofFloat(startY, endY);
+                mAnimation.setDuration(
+                        show ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS);
+
+                mAnimation.addUpdateListener(animation -> {
+                    SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+                    float value = (float) animation.getAnimatedValue();
+                    t.setPosition(mImeSourceControl.getLeash(), x, value);
+                    dispatchPositionChanged(mDisplayId, imeTop(imeSource, value), t);
+                    t.apply();
+                    t.close();
+                });
+                mAnimation.setInterpolator(INTERPOLATOR);
+                mAnimation.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+                        t.setPosition(mImeSourceControl.getLeash(), x, startY);
+                        dispatchStartPositioning(mDisplayId, imeTop(imeSource, startY),
+                                imeTop(imeSource, endY), mAnimationDirection == DIRECTION_SHOW,
+                                t);
+                        if (mAnimationDirection == DIRECTION_SHOW) {
+                            t.show(mImeSourceControl.getLeash());
+                        }
+                        t.apply();
+                        t.close();
+                    }
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+                        t.setPosition(mImeSourceControl.getLeash(), x, endY);
+                        dispatchEndPositioning(mDisplayId, imeTop(imeSource, endY),
+                                mAnimationDirection == DIRECTION_SHOW, t);
+                        if (mAnimationDirection == DIRECTION_HIDE) {
+                            t.hide(mImeSourceControl.getLeash());
+                        }
+                        t.apply();
+                        t.close();
+
+                        mAnimationDirection = DIRECTION_NONE;
+                        mAnimation = null;
+                    }
+                });
+                if (!show) {
+                    // When going away, queue up insets change first, otherwise any bounds changes
+                    // can have a "flicker" of ime-provided insets.
+                    setVisibleDirectly(false /* visible */);
+                }
+                mAnimation.start();
+                if (show) {
+                    // When showing away, queue up insets change last, otherwise any bounds changes
+                    // can have a "flicker" of ime-provided insets.
+                    setVisibleDirectly(true /* visible */);
+                }
+            });
+        }
+    }
+
+    /**
+     * Allows other things to synchronize with the ime position
+     */
+    public interface ImePositionProcessor {
+        /**
+         * Called when the IME position is starting to animate.
+         */
+        void onImeStartPositioning(int displayId, int imeTop, int finalImeTop, boolean showing,
+                SurfaceControl.Transaction t);
+
+        /**
+         * Called when the ime position changed. This is expected to be a synchronous call on the
+         * animation thread. Operations can be added to the transaction to be applied in sync.
+         */
+        void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t);
+
+        /**
+         * Called when the IME position is done animating.
+         */
+        void onImeEndPositioning(int displayId, int imeTop, boolean showing,
+                SurfaceControl.Transaction t);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index b70fdbd..eccf096 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -19,7 +19,7 @@
 
 import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
 import static android.telephony.SubscriptionManager.DATA_ROAMING_ENABLE;
-import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE;
+import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT;
 
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.TestCase.assertFalse;
@@ -83,14 +83,14 @@
     private static final String TEST_CARRIER_2 = "TEST_CARRIER_2";
     private static final int TEST_CARRIER_ID = 1;
     private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(0, "", 0,
-            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "",
             DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, null,
             TEST_CARRIER_ID, 0);
     private static final SubscriptionInfo TEST_SUBSCRIPTION_NULL = new SubscriptionInfo(0, "", 0,
-            TEST_CARRIER, null, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", DATA_ROAMING_DISABLE,
+            TEST_CARRIER, null, NAME_SOURCE_DEFAULT, 0xFFFFFF, "", DATA_ROAMING_DISABLE,
             null, null, null, null, false, null, "");
     private static final SubscriptionInfo TEST_SUBSCRIPTION_ROAMING = new SubscriptionInfo(0, "", 0,
-            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "",
             DATA_ROAMING_ENABLE, null, null, null, null, false, null, "");
     @Mock
     private WifiManager mWifiManager;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 40ea6dd..0c22aae 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -17,7 +17,7 @@
 package com.android.keyguard;
 
 import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
-import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE;
+import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -84,11 +84,11 @@
     private static final int TEST_CARRIER_ID = 1;
     private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24";
     private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(1, "", 0,
-            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "",
             DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
             TEST_CARRIER_ID, 0);
     private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(2, "", 0,
-            TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+            TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT, 0xFFFFFF, "",
             DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
             TEST_CARRIER_ID, 0);
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 40b0ba9..3fdbd3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -47,6 +47,9 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
+import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
@@ -72,6 +75,7 @@
     public static final UserHandle USER_HANDLE = UserHandle.of(ActivityManager.getCurrentUser());
 
     private static final String GROUP_KEY = "gruKey";
+    private static final String APP_NAME = "appName";
 
     private final Context mContext;
     private int mId;
@@ -303,9 +307,6 @@
                 null /* root */,
                 false /* attachToRoot */);
         ExpandableNotificationRow row = mRow;
-        row.setGroupManager(mGroupManager);
-        row.setHeadsUpManager(mHeadsUpManager);
-        row.setAboveShelfChangedListener(aboveShelf -> {});
 
         final NotificationChannel channel =
                 new NotificationChannel(
@@ -329,6 +330,23 @@
         entry.setRow(row);
         entry.createIcons(mContext, entry.getSbn());
         row.setEntry(entry);
+
+        NotificationContentInflater contentBinder = new NotificationContentInflater(
+                mock(NotifRemoteViewCache.class),
+                mock(NotificationRemoteInputManager.class));
+        contentBinder.setInflateSynchronously(true);
+
+        row.initialize(
+                APP_NAME,
+                entry.getKey(),
+                mock(ExpansionLogger.class),
+                mock(KeyguardBypassController.class),
+                mGroupManager,
+                mHeadsUpManager,
+                contentBinder,
+                mock(OnExpandClickListener.class));
+        row.setAboveShelfChangedListener(aboveShelf -> { });
+
         row.setInflationFlags(extraInflationFlags);
         inflateAndWait(row);
 
@@ -341,11 +359,10 @@
 
     private static void inflateAndWait(ExpandableNotificationRow row) throws Exception {
         CountDownLatch countDownLatch = new CountDownLatch(1);
-        row.getNotificationInflater().setInflateSynchronously(true);
         NotificationContentInflater.InflationCallback callback =
                 new NotificationContentInflater.InflationCallback() {
                     @Override
-                    public void handleInflationException(StatusBarNotification notification,
+                    public void handleInflationException(NotificationEntry entry,
                             Exception e) {
                         countDownLatch.countDown();
                     }
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 f55ea4f..1c29453 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
@@ -65,6 +65,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -86,7 +87,10 @@
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -143,6 +147,7 @@
     @Mock private RowInflaterTask mAsyncInflationTask;
     @Mock private NotificationRowBinder mMockedRowBinder;
     @Mock private NotifLog mNotifLog;
+    @Mock private FeatureFlags mFeatureFlags;
 
     private int mId;
     private NotificationEntry mEntry;
@@ -219,6 +224,8 @@
 
         mEntry.expandedIcon = mock(StatusBarIconView.class);
 
+        when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
+        when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
         mEntryManager = new TestableNotificationEntryManager(
                 mNotifLog,
                 mGroupManager,
@@ -230,19 +237,27 @@
                         mNotifLog,
                         mock(NotificationSectionsFeatureManager.class),
                         mock(PeopleNotificationIdentifier.class)),
-                mEnvironment
+                mEnvironment,
+                mFeatureFlags
         );
         mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
         mEntryManager.addNotificationEntryListener(mEntryListener);
         mEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
 
+        NotificationRowContentBinder contentBinder = new NotificationContentInflater(
+                mock(NotifRemoteViewCache.class),
+                mRemoteInputManager);
+
         NotificationRowBinderImpl notificationRowBinder =
-                new NotificationRowBinderImpl(mContext, true, /* allowLongPress */
+                new NotificationRowBinderImpl(mContext,
+                        contentBinder,
+                        true, /* allowLongPress */
                         mock(KeyguardBypassController.class),
                         mock(StatusBarStateController.class),
                         mock(NotificationLogger.class));
         notificationRowBinder.setUpWithPresenter(
-                mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback);
+                mPresenter, mListContainer, mHeadsUpManager, mBindCallback);
+        notificationRowBinder.setInflationCallback(mEntryManager);
         notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
         mEntryManager.setRowBinder(notificationRowBinder);
 
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 34beefe..1afee12 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
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification
 
+import com.android.systemui.statusbar.FeatureFlags
 import com.android.systemui.statusbar.NotificationPresenter
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
@@ -33,8 +34,9 @@
     log: NotifLog,
     gm: NotificationGroupManager,
     rm: NotificationRankingManager,
-    ke: KeyguardEnvironment
-) : NotificationEntryManager(log, gm, rm, ke) {
+    ke: KeyguardEnvironment,
+    ff: FeatureFlags
+) : NotificationEntryManager(log, gm, rm, ke, ff) {
 
     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 0837a42..28feaca 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
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -261,9 +262,13 @@
                 .setRank(5)
                 .setExplanation("baz buzz")
                 .build();
+
+        // WHEN entry3's ranking update includes an update to its overrideGroupKey
+        final String newOverrideGroupKey = "newOverrideGroupKey";
         Ranking newRanking3 = new RankingBuilder(notif3.ranking)
                 .setRank(6)
                 .setExplanation("Penguin pizza")
+                .setOverrideGroupKey(newOverrideGroupKey)
                 .build();
 
         mNoMan.setRanking(notif1.sbn.getKey(), newRanking1);
@@ -275,6 +280,10 @@
         assertEquals(newRanking1, entry1.getRanking());
         assertEquals(newRanking2, entry2.getRanking());
         assertEquals(newRanking3, entry3.getRanking());
+
+        // THEN the entry3's overrideGroupKey is updated along with its groupKey
+        assertEquals(newOverrideGroupKey, entry3.getSbn().getOverrideGroupKey());
+        assertNotNull(entry3.getSbn().getGroupKey());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
index 0764d0c..867a9b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
@@ -178,9 +178,10 @@
 private fun fakePersonModel(
     id: String,
     name: CharSequence,
-    clickIntent: PendingIntent
+    clickIntent: PendingIntent,
+    userId: Int = 0
 ): PersonModel =
-        PersonModel(id, name, mock(Drawable::class.java), clickIntent)
+        PersonModel(id, name, mock(Drawable::class.java), clickIntent, userId)
 
 private fun fakePersonViewModel(name: CharSequence): PersonViewModel =
         PersonViewModel(name, mock(Drawable::class.java), mock({}.javaClass))
@@ -207,4 +208,4 @@
     override fun onDataChanged(data: T) {
         lastSeen = Maybe.Just(data)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
new file mode 100644
index 0000000..d7214f3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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 static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.widget.RemoteViews;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class NotifRemoteViewCacheImplTest extends SysuiTestCase {
+
+    private NotifRemoteViewCacheImpl mNotifRemoteViewCache;
+    private NotificationEntry mEntry;
+    private NotificationEntryListener mEntryListener;
+    @Mock private RemoteViews mRemoteViews;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mEntry = new NotificationEntryBuilder().build();
+
+        NotificationEntryManager entryManager = mock(NotificationEntryManager.class);
+        mNotifRemoteViewCache = new NotifRemoteViewCacheImpl(entryManager);
+        ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
+                ArgumentCaptor.forClass(NotificationEntryListener.class);
+        verify(entryManager).addNotificationEntryListener(entryListenerCaptor.capture());
+        mEntryListener = entryListenerCaptor.getValue();
+    }
+
+    @Test
+    public void testPutCachedView() {
+        // GIVEN an initialized cache for an entry.
+        mEntryListener.onPendingEntryAdded(mEntry);
+
+        // WHEN a notification's cached remote views is put in.
+        mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
+
+        // THEN the remote view is cached.
+        assertTrue(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
+        assertEquals(
+                "Cached remote view is not the one we put in.",
+                mRemoteViews,
+                mNotifRemoteViewCache.getCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
+    }
+
+    @Test
+    public void testRemoveCachedView() {
+        // GIVEN a cache with a cached view.
+        mEntryListener.onPendingEntryAdded(mEntry);
+        mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
+
+        // WHEN we remove the cached view.
+        mNotifRemoteViewCache.removeCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED);
+
+        // THEN the remote view is not cached.
+        assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
+    }
+
+    @Test
+    public void testClearCache() {
+        // GIVEN a non-empty cache.
+        mEntryListener.onPendingEntryAdded(mEntry);
+        mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
+        mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_EXPANDED, mRemoteViews);
+
+        // WHEN we clear the cache.
+        mNotifRemoteViewCache.clearCache(mEntry);
+
+        // THEN the cache is empty.
+        assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
+        assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_EXPANDED));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index dc4e498..cb9da6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -22,30 +22,35 @@
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.Notification;
 import android.content.Context;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.Looper;
-import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
-import android.util.ArrayMap;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.RemoteViews;
+import android.widget.TextView;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.filters.Suppress;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
@@ -58,6 +63,8 @@
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.HashMap;
 import java.util.concurrent.CountDownLatch;
@@ -74,8 +81,11 @@
     private Notification.Builder mBuilder;
     private ExpandableNotificationRow mRow;
 
+    @Mock private NotifRemoteViewCache mCache;
+
     @Before
     public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
         mBuilder = new Notification.Builder(mContext).setSmallIcon(
                 R.drawable.ic_person)
                 .setContentTitle("Title")
@@ -84,7 +94,9 @@
         ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow(
                 mBuilder.build());
         mRow = spy(row);
-        mNotificationInflater = new NotificationContentInflater();
+        mNotificationInflater = new NotificationContentInflater(
+                mCache,
+                mock(NotificationRemoteInputManager.class));
     }
 
     @Test
@@ -175,11 +187,13 @@
                 result,
                 FLAG_CONTENT_VIEW_EXPANDED,
                 0,
-                new ArrayMap() /* cachedContentViews */, mRow,
+                mock(NotifRemoteViewCache.class),
+                mRow.getEntry(),
+                mRow,
                 true /* isNewView */, (v, p, r) -> true,
                 new InflationCallback() {
                     @Override
-                    public void handleInflationException(StatusBarNotification notification,
+                    public void handleInflationException(NotificationEntry entry,
                             Exception e) {
                         countDownLatch.countDown();
                         throw new RuntimeException("No Exception expected");
@@ -245,6 +259,71 @@
                 NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
     }
 
+    @Test
+    public void testUsesSameViewWhenCachedPossibleToReuse() throws Exception {
+        // GIVEN a cached view.
+        RemoteViews contractedRemoteView = mBuilder.createContentView();
+        when(mCache.hasCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
+                .thenReturn(true);
+        when(mCache.getCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
+                .thenReturn(contractedRemoteView);
+
+        // GIVEN existing bound view with same layout id.
+        View view = contractedRemoteView.apply(mContext, null /* parent */);
+        mRow.getPrivateLayout().setContractedChild(view);
+
+        // WHEN inflater inflates
+        inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow);
+
+        // THEN the view should be re-used
+        assertEquals("Binder inflated a new view even though the old one was cached and usable.",
+                view, mRow.getPrivateLayout().getContractedChild());
+    }
+
+    @Test
+    public void testInflatesNewViewWhenCachedNotPossibleToReuse() throws Exception {
+        // GIVEN a cached remote view.
+        RemoteViews contractedRemoteView = mBuilder.createHeadsUpContentView();
+        when(mCache.hasCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
+                .thenReturn(true);
+        when(mCache.getCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
+                .thenReturn(contractedRemoteView);
+
+        // GIVEN existing bound view with different layout id.
+        View view = new TextView(mContext);
+        mRow.getPrivateLayout().setContractedChild(view);
+
+        // WHEN inflater inflates
+        inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow);
+
+        // THEN the view should be a new view
+        assertNotEquals("Binder (somehow) used the same view when inflating.",
+                view, mRow.getPrivateLayout().getContractedChild());
+    }
+
+    @Test
+    public void testInflationCachesCreatedRemoteView() throws Exception {
+        // WHEN inflater inflates
+        inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow);
+
+        // THEN inflater informs cache of the new remote view
+        verify(mCache).putCachedView(
+                eq(mRow.getEntry()),
+                eq(FLAG_CONTENT_VIEW_CONTRACTED),
+                any());
+    }
+
+    @Test
+    public void testUnbindRemovesCachedRemoteView() {
+        // WHEN inflated unbinds content
+        mNotificationInflater.unbindContent(mRow.getEntry(), mRow, FLAG_CONTENT_VIEW_HEADS_UP);
+
+        // THEN inflated informs cache to remove remote view
+        verify(mCache).removeCachedView(
+                eq(mRow.getEntry()),
+                eq(FLAG_CONTENT_VIEW_HEADS_UP));
+    }
+
     private static void inflateAndWait(NotificationContentInflater inflater,
             @InflationFlag int contentToInflate,
             ExpandableNotificationRow row)
@@ -261,7 +340,7 @@
         inflater.setInflateSynchronously(true);
         InflationCallback callback = new InflationCallback() {
             @Override
-            public void handleInflationException(StatusBarNotification notification,
+            public void handleInflationException(NotificationEntry entry,
                     Exception e) {
                 if (!expectingException) {
                     exceptionHolder.setException(e);
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 77a6a26..39f037c 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
@@ -53,6 +53,7 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
@@ -166,7 +167,8 @@
                         mock(NotificationSectionsFeatureManager.class),
                         mock(PeopleNotificationIdentifier.class)
                 ),
-                mock(NotificationEntryManager.KeyguardEnvironment.class));
+                mock(NotificationEntryManager.KeyguardEnvironment.class),
+                mock(FeatureFlags.class));
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 7e485f4..3da87ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -122,6 +122,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
@@ -247,6 +248,7 @@
     @Mock private DismissCallbackRegistry mDismissCallbackRegistry;
     @Mock private ScreenPinningRequest mScreenPinningRequest;
     @Mock private NotificationEntryManager mEntryManager;
+    @Mock private NotificationContentInflater mNotificationContentInflater;
     @Mock private LockscreenLockIconController mLockscreenLockIconController;
     @Mock private StatusBarNotificationActivityStarter.Builder
             mStatusBarNotificationActivityStarterBuilder;
@@ -356,6 +358,7 @@
                 mNotificationGutsManager,
                 notificationLogger,
                 mEntryManager,
+                mNotificationContentInflater,
                 mNotificationInterruptionStateProvider,
                 mNotificationViewHierarchyManager,
                 mKeyguardViewMediator,
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 0be853a..797b713 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -20,7 +20,7 @@
     srcs: [
         "src/**/*.java",
         ":framework-tethering-shared-srcs",
-        ":net-module-utils-srcs",
+        ":tethering-module-utils-srcs",
         ":services-tethering-shared-srcs",
     ],
     static_libs: [
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index a49ab85..11e5718 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -15,8 +15,6 @@
  */
 package android.net;
 
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-
 import android.annotation.NonNull;
 import android.content.Context;
 import android.net.ConnectivityManager.OnTetheringEventCallback;
@@ -52,6 +50,103 @@
     private TetheringConfigurationParcel mTetheringConfiguration;
     private TetherStatesParcel mTetherStatesParcel;
 
+    /**
+     * Broadcast Action: A tetherable connection has come or gone.
+     * Uses {@code TetheringManager.EXTRA_AVAILABLE_TETHER},
+     * {@code TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY},
+     * {@code TetheringManager.EXTRA_ACTIVE_TETHER}, and
+     * {@code TetheringManager.EXTRA_ERRORED_TETHER} to indicate
+     * the current state of tethering.  Each include a list of
+     * interface names in that state (may be empty).
+     */
+    public static final String ACTION_TETHER_STATE_CHANGED =
+            "android.net.conn.TETHER_STATE_CHANGED";
+
+    /**
+     * gives a String[] listing all the interfaces configured for
+     * tethering and currently available for tethering.
+     */
+    public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+
+    /**
+     * gives a String[] listing all the interfaces currently in local-only
+     * mode (ie, has DHCPv4+IPv6-ULA support and no packet forwarding)
+     */
+    public static final String EXTRA_ACTIVE_LOCAL_ONLY = "localOnlyArray";
+
+    /**
+     * gives a String[] listing all the interfaces currently tethered
+     * (ie, has DHCPv4 support and packets potentially forwarded/NATed)
+     */
+    public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+
+    /**
+     * gives a String[] listing all the interfaces we tried to tether and
+     * failed.  Use {@link #getLastTetherError} to find the error code
+     * for any interfaces listed here.
+     */
+    public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+
+    /**
+     * Invalid tethering type.
+     * @see #startTethering.
+     */
+    public static final int TETHERING_INVALID   = -1;
+
+    /**
+     * Wifi tethering type.
+     * @see #startTethering.
+     */
+    public static final int TETHERING_WIFI      = 0;
+
+    /**
+     * USB tethering type.
+     * @see #startTethering.
+     */
+    public static final int TETHERING_USB       = 1;
+
+    /**
+     * Bluetooth tethering type.
+     * @see #startTethering.
+     */
+    public static final int TETHERING_BLUETOOTH = 2;
+
+    /**
+     * Wifi P2p tethering type.
+     * Wifi P2p tethering is set through events automatically, and don't
+     * need to start from #startTethering.
+     */
+    public static final int TETHERING_WIFI_P2P = 3;
+
+    /**
+     * Extra used for communicating with the TetherService. Includes the type of tethering to
+     * enable if any.
+     */
+    public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+
+    /**
+     * Extra used for communicating with the TetherService. Includes the type of tethering for
+     * which to cancel provisioning.
+     */
+    public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+
+    /**
+     * Extra used for communicating with the TetherService. True to schedule a recheck of tether
+     * provisioning.
+     */
+    public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+
+    /**
+     * Tells the TetherService to run a provision check now.
+     */
+    public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+
+    /**
+     * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
+     * which will receive provisioning results. Can be left empty.
+     */
+    public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+
     public static final int TETHER_ERROR_NO_ERROR           = 0;
     public static final int TETHER_ERROR_UNKNOWN_IFACE      = 1;
     public static final int TETHER_ERROR_SERVICE_UNAVAIL    = 2;
@@ -470,7 +565,7 @@
      * failed.  Re-attempting to tether may cause them to reset to the Tethered
      * state.  Alternatively, causing the interface to be destroyed and recreated
      * may cause them to reset to the available state.
-     * {@link ConnectivityManager#getLastTetherError} can be used to get more
+     * {@link TetheringManager#getLastTetherError} can be used to get more
      * information on the cause of the errors.
      *
      * @return an array of 0 or more String indicating the interface names
diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt
index d93531b..c6efa41 100644
--- a/packages/Tethering/jarjar-rules.txt
+++ b/packages/Tethering/jarjar-rules.txt
@@ -11,6 +11,7 @@
 rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1
 rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1
 rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
+rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1
 
 rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1
 
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index abfb33c..6ac467e 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -24,25 +24,24 @@
 import static android.net.util.NetworkConstants.asByte;
 import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
 
-import android.net.ConnectivityManager;
 import android.net.INetd;
 import android.net.INetworkStackStatusCallback;
 import android.net.INetworkStatsService;
-import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.RouteInfo;
+import android.net.TetheringManager;
 import android.net.dhcp.DhcpServerCallbacks;
 import android.net.dhcp.DhcpServingParamsParcel;
 import android.net.dhcp.DhcpServingParamsParcelExt;
 import android.net.dhcp.IDhcpServer;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
+import android.net.shared.NetdUtils;
+import android.net.shared.RouteUtils;
 import android.net.util.InterfaceParams;
 import android.net.util.InterfaceSet;
-import android.net.util.NetdService;
 import android.net.util.SharedLog;
-import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
@@ -119,7 +118,7 @@
          *
          * @param who the calling instance of IpServer
          * @param state one of STATE_*
-         * @param lastError one of ConnectivityManager.TETHER_ERROR_*
+         * @param lastError one of TetheringManager.TETHER_ERROR_*
          */
         public void updateInterfaceState(IpServer who, int state, int lastError) { }
 
@@ -144,10 +143,6 @@
             return InterfaceParams.getByName(ifName);
         }
 
-        public INetd getNetdService() {
-            return NetdService.getInstance();
-        }
-
         /** Create a DhcpServer instance to be used by IpServer. */
         public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
                 DhcpServerCallbacks cb);
@@ -180,7 +175,6 @@
     private final State mUnavailableState;
 
     private final SharedLog mLog;
-    private final INetworkManagementService mNMService;
     private final INetd mNetd;
     private final INetworkStatsService mStatsService;
     private final Callback mCallback;
@@ -210,15 +204,15 @@
     private int mDhcpServerStartIndex = 0;
     private IDhcpServer mDhcpServer;
     private RaParams mLastRaParams;
+    private LinkAddress mIpv4Address;
 
     public IpServer(
             String ifaceName, Looper looper, int interfaceType, SharedLog log,
-            INetworkManagementService nMService, INetworkStatsService statsService,
-            Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
+            INetd netd, INetworkStatsService statsService, Callback callback,
+            boolean usingLegacyDhcp, Dependencies deps) {
         super(ifaceName, looper);
         mLog = log.forSubComponent(ifaceName);
-        mNMService = nMService;
-        mNetd = deps.getNetdService();
+        mNetd = netd;
         mStatsService = statsService;
         mCallback = callback;
         mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog);
@@ -228,7 +222,7 @@
         mUsingLegacyDhcp = usingLegacyDhcp;
         mDeps = deps;
         resetLinkProperties();
-        mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+        mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
         mServingMode = STATE_AVAILABLE;
 
         mInitialState = new InitialState();
@@ -249,7 +243,7 @@
     }
 
     /**
-     * Tethering downstream type. It would be one of ConnectivityManager#TETHERING_*.
+     * Tethering downstream type. It would be one of TetheringManager#TETHERING_*.
      */
     public int interfaceType() {
         return mInterfaceType;
@@ -347,13 +341,13 @@
                         }
                     });
                 } catch (RemoteException e) {
-                    e.rethrowFromSystemServer();
+                    throw new IllegalStateException(e);
                 }
             });
         }
 
         private void handleError() {
-            mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR;
+            mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
             transitionTo(mInitialState);
         }
     }
@@ -388,14 +382,15 @@
                     public void callback(int statusCode) {
                         if (statusCode != STATUS_SUCCESS) {
                             mLog.e("Error stopping DHCP server: " + statusCode);
-                            mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR;
+                            mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
                             // Not much more we can do here
                         }
                     }
                 });
                 mDhcpServer = null;
             } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
+                mLog.e("Error stopping DHCP", e);
+                // Not much more we can do here
             }
         }
     }
@@ -414,85 +409,69 @@
         // NOTE: All of configureIPv4() will be refactored out of existence
         // into calls to InterfaceController, shared with startIPv4().
         mInterfaceCtrl.clearIPv4Address();
+        mIpv4Address = null;
     }
 
-    // TODO: Refactor this in terms of calls to InterfaceController.
     private boolean configureIPv4(boolean enabled) {
         if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
 
         // TODO: Replace this hard-coded information with dynamically selected
         // config passed down to us by a higher layer IP-coordinating element.
-        String ipAsString = null;
+        final Inet4Address srvAddr;
         int prefixLen = 0;
-        if (mInterfaceType == ConnectivityManager.TETHERING_USB) {
-            ipAsString = USB_NEAR_IFACE_ADDR;
-            prefixLen = USB_PREFIX_LENGTH;
-        } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
-            ipAsString = getRandomWifiIPv4Address();
-            prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
-        } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI_P2P) {
-            ipAsString = WIFI_P2P_IFACE_ADDR;
-            prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH;
-        } else {
-            // BT configures the interface elsewhere: only start DHCP.
-            final Inet4Address srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR);
-            return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
+        try {
+            if (mInterfaceType == TetheringManager.TETHERING_USB) {
+                srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR);
+                prefixLen = USB_PREFIX_LENGTH;
+            } else if (mInterfaceType == TetheringManager.TETHERING_WIFI) {
+                srvAddr = (Inet4Address) parseNumericAddress(getRandomWifiIPv4Address());
+                prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
+            } else if (mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) {
+                srvAddr = (Inet4Address) parseNumericAddress(WIFI_P2P_IFACE_ADDR);
+                prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH;
+            } else {
+                // BT configures the interface elsewhere: only start DHCP.
+                // TODO: make all tethering types behave the same way, and delete the bluetooth
+                // code that calls into NetworkManagementService directly.
+                srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR);
+                mIpv4Address = new LinkAddress(srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
+                return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
+            }
+            mIpv4Address = new LinkAddress(srvAddr, prefixLen);
+        } catch (IllegalArgumentException e) {
+            mLog.e("Error selecting ipv4 address", e);
+            if (!enabled) stopDhcp();
+            return false;
         }
 
-        final LinkAddress linkAddr;
-        try {
-            final InterfaceConfiguration ifcg = mNMService.getInterfaceConfig(mIfaceName);
-            if (ifcg == null) {
-                mLog.e("Received null interface config");
-                return false;
-            }
+        final Boolean setIfaceUp;
+        if (mInterfaceType == TetheringManager.TETHERING_WIFI) {
+            // The WiFi stack has ownership of the interface up/down state.
+            // It is unclear whether the Bluetooth or USB stacks will manage their own
+            // state.
+            setIfaceUp = null;
+        } else {
+            setIfaceUp = enabled;
+        }
+        if (!mInterfaceCtrl.setInterfaceConfiguration(mIpv4Address, setIfaceUp)) {
+            mLog.e("Error configuring interface");
+            if (!enabled) stopDhcp();
+            return false;
+        }
 
-            InetAddress addr = parseNumericAddress(ipAsString);
-            linkAddr = new LinkAddress(addr, prefixLen);
-            ifcg.setLinkAddress(linkAddr);
-            if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
-                // The WiFi stack has ownership of the interface up/down state.
-                // It is unclear whether the Bluetooth or USB stacks will manage their own
-                // state.
-                ifcg.ignoreInterfaceUpDownStatus();
-            } else {
-                if (enabled) {
-                    ifcg.setInterfaceUp();
-                } else {
-                    ifcg.setInterfaceDown();
-                }
-            }
-            ifcg.clearFlag("running");
-
-            // TODO: this may throw if the interface is already gone. Do proper handling and
-            // simplify the DHCP server start/stop.
-            mNMService.setInterfaceConfig(mIfaceName, ifcg);
-
-            if (!configureDhcp(enabled, (Inet4Address) addr, prefixLen)) {
-                return false;
-            }
-        } catch (Exception e) {
-            mLog.e("Error configuring interface " + e);
-            if (!enabled) {
-                try {
-                    // Calling stopDhcp several times is fine
-                    stopDhcp();
-                } catch (Exception dhcpError) {
-                    mLog.e("Error stopping DHCP", dhcpError);
-                }
-            }
+        if (!configureDhcp(enabled, srvAddr, prefixLen)) {
             return false;
         }
 
         // Directly-connected route.
-        final IpPrefix ipv4Prefix = new IpPrefix(linkAddr.getAddress(),
-                linkAddr.getPrefixLength());
+        final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(),
+                mIpv4Address.getPrefixLength());
         final RouteInfo route = new RouteInfo(ipv4Prefix, null, null, RTN_UNICAST);
         if (enabled) {
-            mLinkProperties.addLinkAddress(linkAddr);
+            mLinkProperties.addLinkAddress(mIpv4Address);
             mLinkProperties.addRoute(route);
         } else {
-            mLinkProperties.removeLinkAddress(linkAddr);
+            mLinkProperties.removeLinkAddress(mIpv4Address);
             mLinkProperties.removeRoute(route);
         }
         return true;
@@ -584,14 +563,12 @@
         if (!deprecatedPrefixes.isEmpty()) {
             final ArrayList<RouteInfo> toBeRemoved =
                     getLocalRoutesFor(mIfaceName, deprecatedPrefixes);
-            try {
-                final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved);
-                if (removalFailures > 0) {
-                    mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
-                            removalFailures));
-                }
-            } catch (RemoteException e) {
-                mLog.e("Failed to remove IPv6 routes from local table: " + e);
+            // Remove routes from local network.
+            final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork(
+                    mNetd, toBeRemoved);
+            if (removalFailures > 0) {
+                mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
+                        removalFailures));
             }
 
             for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
@@ -608,13 +585,18 @@
                 final ArrayList<RouteInfo> toBeAdded =
                         getLocalRoutesFor(mIfaceName, addedPrefixes);
                 try {
-                    // It's safe to call addInterfaceToLocalNetwork() even if
-                    // the interface is already in the local_network. Note also
-                    // that adding routes that already exist does not cause an
-                    // error (EEXIST is silently ignored).
-                    mNMService.addInterfaceToLocalNetwork(mIfaceName, toBeAdded);
-                } catch (Exception e) {
-                    mLog.e("Failed to add IPv6 routes to local table: " + e);
+                    // It's safe to call networkAddInterface() even if
+                    // the interface is already in the local_network.
+                    mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName);
+                    try {
+                        // Add routes from local network. Note that adding routes that
+                        // already exist does not cause an error (EEXIST is silently ignored).
+                        RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
+                    } catch (IllegalStateException e) {
+                        mLog.e("Failed to add IPv6 routes to local table: " + e);
+                    }
+                } catch (ServiceSpecificException | RemoteException e) {
+                    mLog.e("Failed to add " + mIfaceName + " to local table: ", e);
                 }
 
                 for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
@@ -728,7 +710,7 @@
             logMessage(this, message.what);
             switch (message.what) {
                 case CMD_TETHER_REQUESTED:
-                    mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+                    mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
                     switch (message.arg1) {
                         case STATE_LOCAL_ONLY:
                             transitionTo(mLocalHotspotState);
@@ -757,15 +739,17 @@
         @Override
         public void enter() {
             if (!startIPv4()) {
-                mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
+                mLastError = TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
                 return;
             }
 
             try {
-                mNMService.tetherInterface(mIfaceName);
-            } catch (Exception e) {
+                final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(),
+                        mIpv4Address.getPrefixLength());
+                NetdUtils.tetherInterface(mNetd, mIfaceName, ipv4Prefix);
+            } catch (RemoteException | ServiceSpecificException e) {
                 mLog.e("Error Tethering: " + e);
-                mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+                mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
                 return;
             }
 
@@ -784,9 +768,9 @@
             stopIPv6();
 
             try {
-                mNMService.untetherInterface(mIfaceName);
-            } catch (Exception e) {
-                mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
+                NetdUtils.untetherInterface(mNetd, mIfaceName);
+            } catch (RemoteException | ServiceSpecificException e) {
+                mLastError = TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
                 mLog.e("Failed to untether interface: " + e);
             }
 
@@ -816,7 +800,7 @@
                 case CMD_START_TETHERING_ERROR:
                 case CMD_STOP_TETHERING_ERROR:
                 case CMD_SET_DNS_FORWARDERS_ERROR:
-                    mLastError = ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+                    mLastError = TetheringManager.TETHER_ERROR_MASTER_ERROR;
                     transitionTo(mInitialState);
                     break;
                 default:
@@ -835,7 +819,7 @@
         @Override
         public void enter() {
             super.enter();
-            if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+            if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) {
                 transitionTo(mInitialState);
             }
 
@@ -871,7 +855,7 @@
         @Override
         public void enter() {
             super.enter();
-            if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+            if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) {
                 transitionTo(mInitialState);
             }
 
@@ -901,17 +885,17 @@
                 // About to tear down NAT; gather remaining statistics.
                 mStatsService.forceUpdate();
             } catch (Exception e) {
-                if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
+                mLog.e("Exception in forceUpdate: " + e.toString());
             }
             try {
-                mNMService.stopInterfaceForwarding(mIfaceName, upstreamIface);
-            } catch (Exception e) {
-                if (VDBG) Log.e(TAG, "Exception in removeInterfaceForward: " + e.toString());
+                mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface);
+            } catch (RemoteException | ServiceSpecificException e) {
+                mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString());
             }
             try {
-                mNMService.disableNat(mIfaceName, upstreamIface);
-            } catch (Exception e) {
-                if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
+                mNetd.tetherRemoveForward(mIfaceName, upstreamIface);
+            } catch (RemoteException | ServiceSpecificException e) {
+                mLog.e("Exception in disableNat: " + e.toString());
             }
         }
 
@@ -947,12 +931,12 @@
 
                     for (String ifname : added) {
                         try {
-                            mNMService.enableNat(mIfaceName, ifname);
-                            mNMService.startInterfaceForwarding(mIfaceName, ifname);
-                        } catch (Exception e) {
-                            mLog.e("Exception enabling NAT: " + e);
+                            mNetd.tetherAddForward(mIfaceName, ifname);
+                            mNetd.ipfwdAddInterfaceForward(mIfaceName, ifname);
+                        } catch (RemoteException | ServiceSpecificException e) {
+                            mLog.e("Exception enabling NAT: " + e.toString());
                             cleanupUpstream();
-                            mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+                            mLastError = TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR;
                             transitionTo(mInitialState);
                             return true;
                         }
@@ -997,7 +981,7 @@
     class UnavailableState extends State {
         @Override
         public void enter() {
-            mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+            mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
             sendInterfaceState(STATE_UNAVAILABLE);
         }
     }
diff --git a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index bba61d7..6f017dc 100644
--- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -668,7 +668,7 @@
     }
 
     private final class UnicastResponder extends Thread {
-        private final InetSocketAddress mSolicitor = new InetSocketAddress();
+        private final InetSocketAddress mSolicitor = new InetSocketAddress(0);
         // The recycled buffer for receiving Router Solicitations from clients.
         // If the RS is larger than IPV6_MIN_MTU the packets are truncated.
         // This is fine since currently only byte 0 is examined anyway.
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
index 7e685fb..4e2a2c1 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -16,16 +16,16 @@
 
 package com.android.server.connectivity.tethering;
 
-import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
-import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
-import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
-import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
-import static android.net.ConnectivityManager.TETHERING_INVALID;
-import static android.net.ConnectivityManager.TETHERING_USB;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
+import static android.net.TetheringManager.EXTRA_ADD_TETHER_TYPE;
+import static android.net.TetheringManager.EXTRA_PROVISION_CALLBACK;
+import static android.net.TetheringManager.EXTRA_RUN_PROVISION;
+import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_INVALID;
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
 
 import static com.android.internal.R.string.config_wifi_tether_enable;
 
@@ -59,7 +59,7 @@
 
 /**
  * Re-check tethering provisioning for enabled downstream tether types.
- * Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
+ * Reference TetheringManager.TETHERING_{@code *} for each tether type.
  *
  * All methods of this class must be accessed from the thread of tethering
  * state machine.
@@ -86,9 +86,9 @@
     private static final int EVENT_GET_ENTITLEMENT_VALUE = 4;
 
     // The ArraySet contains enabled downstream types, ex:
-    // {@link ConnectivityManager.TETHERING_WIFI}
-    // {@link ConnectivityManager.TETHERING_USB}
-    // {@link ConnectivityManager.TETHERING_BLUETOOTH}
+    // {@link TetheringManager.TETHERING_WIFI}
+    // {@link TetheringManager.TETHERING_USB}
+    // {@link TetheringManager.TETHERING_BLUETOOTH}
     private final ArraySet<Integer> mCurrentTethers;
     private final Context mContext;
     private final int mPermissionChangeMessageCode;
@@ -96,8 +96,8 @@
     private final SparseIntArray mEntitlementCacheValue;
     private final EntitlementHandler mHandler;
     private final StateMachine mTetherMasterSM;
-    // Key: ConnectivityManager.TETHERING_*(downstream).
-    // Value: ConnectivityManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result).
+    // Key: TetheringManager.TETHERING_*(downstream).
+    // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result).
     private final SparseIntArray mCellularPermitted;
     private PendingIntent mProvisioningRecheckAlarm;
     private boolean mCellularUpstreamPermitted = true;
@@ -133,7 +133,7 @@
         /**
          * Ui entitlement check fails in |downstream|.
          *
-         * @param downstream tethering type from ConnectivityManager.TETHERING_{@code *}.
+         * @param downstream tethering type from TetheringManager.TETHERING_{@code *}.
          */
         void onUiEntitlementFailed(int downstream);
     }
@@ -169,7 +169,7 @@
      * This is called when tethering starts.
      * Launch provisioning app if upstream is cellular.
      *
-     * @param downstreamType tethering type from ConnectivityManager.TETHERING_{@code *}
+     * @param downstreamType tethering type from TetheringManager.TETHERING_{@code *}
      * @param showProvisioningUi a boolean indicating whether to show the
      *        provisioning app UI if there is one.
      */
@@ -210,7 +210,7 @@
     /**
      * Tell EntitlementManager that a given type of tethering has been disabled
      *
-     * @param type tethering type from ConnectivityManager.TETHERING_{@code *}
+     * @param type tethering type from TetheringManager.TETHERING_{@code *}
      */
     public void stopProvisioningIfNeeded(int type) {
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_STOP_PROVISIONING, type, 0));
@@ -296,7 +296,7 @@
 
     /**
      * Re-check tethering provisioning for all enabled tether types.
-     * Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
+     * Reference TetheringManager.TETHERING_{@code *} for each tether type.
      *
      * @param config an object that encapsulates the various tethering configuration elements.
      * Note: this method is only called from TetherMaster on the handler thread.
@@ -363,7 +363,7 @@
 
     /**
      * Run no UI tethering provisioning check.
-     * @param type tethering type from ConnectivityManager.TETHERING_{@code *}
+     * @param type tethering type from TetheringManager.TETHERING_{@code *}
      * @param subId default data subscription ID.
      */
     @VisibleForTesting
@@ -390,7 +390,7 @@
 
     /**
      * Run the UI-enabled tethering provisioning check.
-     * @param type tethering type from ConnectivityManager.TETHERING_{@code *}
+     * @param type tethering type from TetheringManager.TETHERING_{@code *}
      * @param subId default data subscription ID.
      * @param receiver to receive entitlement check result.
      */
@@ -398,7 +398,7 @@
     protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) {
         if (DBG) mLog.i("runUiTetherProvisioning: " + type);
 
-        Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
+        Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI);
         intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
         intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
         intent.putExtra(EXTRA_SUBID, subId);
@@ -461,7 +461,7 @@
      * Add the mapping between provisioning result and tethering type.
      * Notify UpstreamNetworkMonitor if Cellular permission changes.
      *
-     * @param type tethering type from ConnectivityManager.TETHERING_{@code *}
+     * @param type tethering type from TetheringManager.TETHERING_{@code *}
      * @param resultCode Provisioning result
      */
     protected void addDownstreamMapping(int type, int resultCode) {
@@ -476,7 +476,7 @@
 
     /**
      * Remove the mapping for input tethering type.
-     * @param type tethering type from ConnectivityManager.TETHERING_{@code *}
+     * @param type tethering type from TetheringManager.TETHERING_{@code *}
      */
     protected void removeDownstreamMapping(int type) {
         mLog.i("removeDownstreamMapping: " + type);
@@ -625,7 +625,7 @@
     /**
      * Update the last entitlement value to internal cache
      *
-     * @param type tethering type from ConnectivityManager.TETHERING_{@code *}
+     * @param type tethering type from TetheringManager.TETHERING_{@code *}
      * @param resultCode last entitlement value
      * @return the last updated entitlement value
      */
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
index 38fa91e..ce7c2a6 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
@@ -279,7 +279,7 @@
                 entry.iface = kv.getKey();
                 entry.rxBytes = value.rxBytes;
                 entry.txBytes = value.txBytes;
-                stats.addValues(entry);
+                stats.addEntry(entry);
             }
 
             return stats;
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 5b26704..d6abfb9 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -20,23 +20,23 @@
 import static android.hardware.usb.UsbManager.USB_CONFIGURED;
 import static android.hardware.usb.UsbManager.USB_CONNECTED;
 import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
-import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
-import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
-import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
-import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
-import static android.net.ConnectivityManager.EXTRA_ERRORED_TETHER;
 import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
-import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
-import static android.net.ConnectivityManager.TETHERING_INVALID;
-import static android.net.ConnectivityManager.TETHERING_USB;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static android.net.ConnectivityManager.TETHERING_WIFI_P2P;
-import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
+import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
+import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER;
+import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER;
+import static android.net.TetheringManager.EXTRA_ERRORED_TETHER;
+import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_INVALID;
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHERING_WIFI_P2P;
+import static android.net.TetheringManager.TETHER_ERROR_MASTER_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
+import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
+import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
 import static android.net.util.TetheringMessageBase.BASE_MASTER;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
@@ -71,10 +71,10 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkInfo;
-import android.net.NetworkUtils;
 import android.net.TetherStatesParcel;
 import android.net.TetheringConfigurationParcel;
 import android.net.ip.IpServer;
+import android.net.shared.NetdUtils;
 import android.net.util.BaseNetdUnsolicitedEventListener;
 import android.net.util.InterfaceSet;
 import android.net.util.PrefixUtils;
@@ -87,12 +87,12 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.ServiceSpecificException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.telephony.PhoneStateListener;
@@ -102,6 +102,9 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
@@ -139,6 +142,8 @@
     };
     private static final SparseArray<String> sMagicDecoderRing =
             MessageUtils.findMessageNames(sMessageClasses);
+    // Keep in sync with NETID_UNSET in system/netd/include/netid_client.h
+    private static final int NETID_UNSET = 0;
 
     private static class TetherState {
         public final IpServer ipServer;
@@ -172,8 +177,6 @@
     private final Context mContext;
     private final ArrayMap<String, TetherState> mTetherStates;
     private final BroadcastReceiver mStateReceiver;
-    // Stopship: replace mNMService before production.
-    private final INetworkManagementService mNMService;
     private final INetworkStatsService mStatsService;
     private final INetworkPolicyManager mPolicyManager;
     private final Looper mLooper;
@@ -210,7 +213,6 @@
         mLog.mark("Tethering.constructed");
         mDeps = deps;
         mContext = mDeps.getContext();
-        mNMService = mDeps.getINetworkManagementService();
         mStatsService = mDeps.getINetworkStatsService();
         mPolicyManager = mDeps.getINetworkPolicyManager();
         mNetd = mDeps.getINetd(mContext);
@@ -225,10 +227,9 @@
 
         mHandler = mTetherMasterSM.getHandler();
         mOffloadController = new OffloadController(mHandler,
-                mDeps.getOffloadHardwareInterface(mHandler, mLog),
-                mContext.getContentResolver(), mNMService,
-                mLog);
-        mUpstreamNetworkMonitor = deps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
+                mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(),
+                mDeps.getINetworkManagementService(), mLog);
+        mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
                 TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
         mForwardedDownstreams = new HashSet<>();
 
@@ -289,13 +290,6 @@
         filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
         filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
         mContext.registerReceiver(mStateReceiver, filter, null, handler);
-
-        filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_MEDIA_SHARED);
-        filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
-        filter.addDataScheme("file");
-        mContext.registerReceiver(mStateReceiver, filter, null, handler);
-
     }
 
     private class TetheringThreadExecutor implements Executor {
@@ -421,7 +415,6 @@
         }
     }
 
-
     void interfaceRemoved(String iface) {
         if (VDBG) Log.d(TAG, "interfaceRemoved " + iface);
         synchronized (mPublicSync) {
@@ -1022,8 +1015,8 @@
 
         String[] ifaces = null;
         try {
-            ifaces = mNMService.listInterfaces();
-        } catch (Exception e) {
+            ifaces = mNetd.interfaceGetList();
+        } catch (RemoteException | ServiceSpecificException e) {
             Log.e(TAG, "Error listing Interfaces", e);
             return;
         }
@@ -1282,25 +1275,25 @@
         protected boolean turnOnMasterTetherSettings() {
             final TetheringConfiguration cfg = mConfig;
             try {
-                mNMService.setIpForwardingEnabled(true);
-            } catch (Exception e) {
+                mNetd.ipfwdEnableForwarding(TAG);
+            } catch (RemoteException | ServiceSpecificException e) {
                 mLog.e(e);
                 transitionTo(mSetIpForwardingEnabledErrorState);
                 return false;
             }
+
             // TODO: Randomize DHCPv4 ranges, especially in hotspot mode.
             // Legacy DHCP server is disabled if passed an empty ranges array
             final String[] dhcpRanges = cfg.enableLegacyDhcpServer
-                    ? cfg.legacyDhcpRanges
-                    : new String[0];
+                    ? cfg.legacyDhcpRanges : new String[0];
             try {
-                // TODO: Find a more accurate method name (startDHCPv4()?).
-                mNMService.startTethering(dhcpRanges);
-            } catch (Exception e) {
+                NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges);
+            } catch (RemoteException | ServiceSpecificException e) {
                 try {
-                    mNMService.stopTethering();
-                    mNMService.startTethering(dhcpRanges);
-                } catch (Exception ee) {
+                    // Stop and retry.
+                    mNetd.tetherStop();
+                    NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges);
+                } catch (RemoteException | ServiceSpecificException ee) {
                     mLog.e(ee);
                     transitionTo(mStartTetheringErrorState);
                     return false;
@@ -1312,15 +1305,15 @@
 
         protected boolean turnOffMasterTetherSettings() {
             try {
-                mNMService.stopTethering();
-            } catch (Exception e) {
+                mNetd.tetherStop();
+            } catch (RemoteException | ServiceSpecificException e) {
                 mLog.e(e);
                 transitionTo(mStopTetheringErrorState);
                 return false;
             }
             try {
-                mNMService.setIpForwardingEnabled(false);
-            } catch (Exception e) {
+                mNetd.ipfwdDisableForwarding(TAG);
+            } catch (RemoteException | ServiceSpecificException e) {
                 mLog.e(e);
                 transitionTo(mSetIpForwardingDisabledErrorState);
                 return false;
@@ -1383,19 +1376,25 @@
 
         protected void setDnsForwarders(final Network network, final LinkProperties lp) {
             // TODO: Set v4 and/or v6 DNS per available connectivity.
-            String[] dnsServers = mConfig.defaultIPv4DNS;
             final Collection<InetAddress> dnses = lp.getDnsServers();
             // TODO: Properly support the absence of DNS servers.
+            final String[] dnsServers;
             if (dnses != null && !dnses.isEmpty()) {
-                // TODO: remove this invocation of NetworkUtils.makeStrings().
-                dnsServers = NetworkUtils.makeStrings(dnses);
+                dnsServers = new String[dnses.size()];
+                int i = 0;
+                for (InetAddress dns : dnses) {
+                    dnsServers[i++] = dns.getHostAddress();
+                }
+            } else {
+                dnsServers = mConfig.defaultIPv4DNS;
             }
+            final int netId = (network != null) ? network.netId : NETID_UNSET;
             try {
-                mNMService.setDnsForwarders(network, dnsServers);
+                mNetd.tetherDnsSet(netId, dnsServers);
                 mLog.log(String.format(
                         "SET DNS forwarders: network=%s dnsServers=%s",
                         network, Arrays.toString(dnsServers)));
-            } catch (Exception e) {
+            } catch (RemoteException | ServiceSpecificException e) {
                 // TODO: Investigate how this can fail and what exactly
                 // happens if/when such failures occur.
                 mLog.e("setting DNS forwarders failed, " + e);
@@ -1698,8 +1697,8 @@
                 Log.e(TAG, "Error in startTethering");
                 notify(IpServer.CMD_START_TETHERING_ERROR);
                 try {
-                    mNMService.setIpForwardingEnabled(false);
-                } catch (Exception e) { }
+                    mNetd.ipfwdDisableForwarding(TAG);
+                } catch (RemoteException | ServiceSpecificException e) { }
             }
         }
 
@@ -1709,8 +1708,8 @@
                 Log.e(TAG, "Error in stopTethering");
                 notify(IpServer.CMD_STOP_TETHERING_ERROR);
                 try {
-                    mNMService.setIpForwardingEnabled(false);
-                } catch (Exception e) { }
+                    mNetd.ipfwdDisableForwarding(TAG);
+                } catch (RemoteException | ServiceSpecificException e) { }
             }
         }
 
@@ -1720,11 +1719,11 @@
                 Log.e(TAG, "Error in setDnsForwarders");
                 notify(IpServer.CMD_SET_DNS_FORWARDERS_ERROR);
                 try {
-                    mNMService.stopTethering();
-                } catch (Exception e) { }
+                    mNetd.tetherStop();
+                } catch (RemoteException | ServiceSpecificException e) { }
                 try {
-                    mNMService.setIpForwardingEnabled(false);
-                } catch (Exception e) { }
+                    mNetd.ipfwdDisableForwarding(TAG);
+                } catch (RemoteException | ServiceSpecificException e) { }
             }
         }
 
@@ -1884,7 +1883,7 @@
         }
     }
 
-    void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+    void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) {
         // Binder.java closes the resource for us.
         @SuppressWarnings("resource")
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
@@ -2065,7 +2064,7 @@
 
         mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
         final TetherState tetherState = new TetherState(
-                new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
+                new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mStatsService,
                              makeControlCallback(), mConfig.enableLegacyDhcpServer,
                              mDeps.getIpServerDependencies()));
         mTetherStates.put(iface, tetherState);
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 490614b..397ba8a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -37,7 +37,6 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
-import android.net.ConnectivityManager;
 import android.net.TetheringConfigurationParcel;
 import android.net.util.SharedLog;
 import android.provider.Settings;
@@ -179,8 +178,8 @@
 
         pw.print("chooseUpstreamAutomatically: ");
         pw.println(chooseUpstreamAutomatically);
-        dumpStringArray(pw, "preferredUpstreamIfaceTypes",
-                preferredUpstreamNames(preferredUpstreamIfaceTypes));
+        pw.print("legacyPreredUpstreamIfaceTypes: ");
+        pw.println(Arrays.toString(toIntArray(preferredUpstreamIfaceTypes)));
 
         dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges);
         dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);
@@ -205,7 +204,7 @@
         sj.add(String.format("isDunRequired:%s", isDunRequired));
         sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically));
         sj.add(String.format("preferredUpstreamIfaceTypes:%s",
-                makeString(preferredUpstreamNames(preferredUpstreamIfaceTypes))));
+                toIntArray(preferredUpstreamIfaceTypes)));
         sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
         sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
         sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer));
@@ -234,21 +233,6 @@
         return sj.toString();
     }
 
-    private static String[] preferredUpstreamNames(Collection<Integer> upstreamTypes) {
-        String[] upstreamNames = null;
-
-        if (upstreamTypes != null) {
-            upstreamNames = new String[upstreamTypes.size()];
-            int i = 0;
-            for (Integer netType : upstreamTypes) {
-                upstreamNames[i] = ConnectivityManager.getNetworkTypeName(netType);
-                i++;
-            }
-        }
-
-        return upstreamNames;
-    }
-
     /** Check whether dun is required. */
     public static boolean checkDunRequired(Context ctx) {
         final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
@@ -388,6 +372,15 @@
         return false;
     }
 
+    private static int[] toIntArray(Collection<Integer> values) {
+        final int[] result = new int[values.size()];
+        int index = 0;
+        for (Integer value : values) {
+            result[index++] = value;
+        }
+        return result;
+    }
+
     /**
      * Convert this TetheringConfiguration to a TetheringConfigurationParcel.
      */
@@ -400,12 +393,7 @@
         parcel.isDunRequired = isDunRequired;
         parcel.chooseUpstreamAutomatically = chooseUpstreamAutomatically;
 
-        int[] preferredTypes = new int[preferredUpstreamIfaceTypes.size()];
-        int index = 0;
-        for (Integer type : preferredUpstreamIfaceTypes) {
-            preferredTypes[index++] = type;
-        }
-        parcel.preferredUpstreamIfaceTypes = preferredTypes;
+        parcel.preferredUpstreamIfaceTypes = toIntArray(preferredUpstreamIfaceTypes);
 
         parcel.legacyDhcpRanges = legacyDhcpRanges;
         parcel.defaultIPv4DNS = defaultIPv4DNS;
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
index 6334c20..d5cdd8a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
@@ -22,14 +22,17 @@
 import android.net.RouteInfo;
 import android.net.util.InterfaceSet;
 
-import java.net.Inet4Address;
-import java.net.Inet6Address;
 import java.net.InetAddress;
+import java.net.UnknownHostException;
 
 /**
  * @hide
  */
 public final class TetheringInterfaceUtils {
+    private static final InetAddress IN6ADDR_ANY = getByAddress(
+            new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
+    private static final InetAddress INADDR_ANY = getByAddress(new byte[] {0, 0, 0, 0});
+
     /**
      * Get upstream interfaces for tethering based on default routes for IPv4/IPv6.
      * @return null if there is no usable interface, or a set of at least one interface otherwise.
@@ -40,7 +43,7 @@
         }
 
         final LinkProperties lp = ns.linkProperties;
-        final String if4 = getInterfaceForDestination(lp, Inet4Address.ANY);
+        final String if4 = getInterfaceForDestination(lp, INADDR_ANY);
         final String if6 = getIPv6Interface(ns);
 
         return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6);
@@ -76,7 +79,7 @@
                 && ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
 
         return canTether
-                ? getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY)
+                ? getInterfaceForDestination(ns.linkProperties, IN6ADDR_ANY)
                 : null;
     }
 
@@ -86,4 +89,12 @@
                 : null;
         return (ri != null) ? ri.getInterface() : null;
     }
+
+    private static InetAddress getByAddress(final byte[] addr) {
+        try {
+            return InetAddress.getByAddress(null, addr);
+        } catch (UnknownHostException e) {
+            throw new AssertionError("illegal address length" + addr.length);
+        }
+    }
 }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index 775484e..e4e4a09 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -26,11 +26,11 @@
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
-import android.net.ConnectivityManager;
 import android.net.IIntResultListener;
 import android.net.INetworkStackConnector;
 import android.net.ITetheringConnector;
 import android.net.ITetheringEventCallback;
+import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.dhcp.DhcpServerCallbacks;
 import android.net.dhcp.DhcpServingParamsParcel;
@@ -307,9 +307,15 @@
             mDeps = new TetheringDependencies() {
                 @Override
                 public NetworkRequest getDefaultNetworkRequest() {
-                    ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
-                            Context.CONNECTIVITY_SERVICE);
-                    return cm.getDefaultRequest();
+                    // TODO: b/147280869, add a proper system API to replace this.
+                    final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder()
+                            .clearCapabilities()
+                            .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                            .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+                            .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+                            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                            .build();
+                    return trackDefaultRequest;
                 }
 
                 @Override
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 22150f6..fe3f517 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -19,7 +19,6 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
 import static android.net.ConnectivityManager.TYPE_NONE;
-import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -35,7 +34,6 @@
 import android.net.util.PrefixUtils;
 import android.net.util.SharedLog;
 import android.os.Handler;
-import android.os.Process;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -130,15 +128,15 @@
      */
     public void startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest,
             EntitlementManager entitle) {
-        // This is not really a "request", just a way of tracking the system default network.
-        // It's guaranteed not to actually bring up any networks because it's the same request
-        // as the ConnectivityService default request, and thus shares fate with it. We can't
-        // use registerDefaultNetworkCallback because it will not track the system default
-        // network if there is a VPN that applies to our UID.
+
+        // defaultNetworkRequest is not really a "request", just a way of tracking the system
+        // default network. It's guaranteed not to actually bring up any networks because it's
+        // the should be the same request as the ConnectivityService default request, and thus
+        // shares fate with it. We can't use registerDefaultNetworkCallback because it will not
+        // track the system default network if there is a VPN that applies to our UID.
         if (mDefaultNetworkCallback == null) {
-            final NetworkRequest trackDefaultRequest = new NetworkRequest(defaultNetworkRequest);
             mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
-            cm().requestNetwork(trackDefaultRequest, mDefaultNetworkCallback, mHandler);
+            cm().requestNetwork(defaultNetworkRequest, mDefaultNetworkCallback, mHandler);
         }
         if (mEntitlementMgr == null) {
             mEntitlementMgr = entitle;
@@ -239,7 +237,7 @@
         final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType(
                 mNetworkMap.values(), preferredTypes, isCellularUpstreamPermitted());
 
-        mLog.log("preferred upstream type: " + getNetworkTypeName(typeStatePair.type));
+        mLog.log("preferred upstream type: " + typeStatePair.type);
 
         switch (typeStatePair.type) {
             case TYPE_MOBILE_DUN:
@@ -514,16 +512,13 @@
             try {
                 nc = ConnectivityManager.networkCapabilitiesForType(type);
             } catch (IllegalArgumentException iae) {
-                Log.e(TAG, "No NetworkCapabilities mapping for legacy type: "
-                        + ConnectivityManager.getNetworkTypeName(type));
+                Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " + type);
                 continue;
             }
             if (!isCellularUpstreamPermitted && isCellular(nc)) {
                 continue;
             }
 
-            nc.setSingleUid(Process.myUid());
-
             for (UpstreamNetworkState value : netStates) {
                 if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) {
                     continue;
diff --git a/packages/Tethering/tests/unit/jarjar-rules.txt b/packages/Tethering/tests/unit/jarjar-rules.txt
index 64fdebd..921fbed 100644
--- a/packages/Tethering/tests/unit/jarjar-rules.txt
+++ b/packages/Tethering/tests/unit/jarjar-rules.txt
@@ -7,5 +7,6 @@
 rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1
 rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1
 rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
+rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1
 
 rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index fd2f708..65a0ac1 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -16,13 +16,14 @@
 
 package android.net.ip;
 
-import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
-import static android.net.ConnectivityManager.TETHERING_USB;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static android.net.ConnectivityManager.TETHERING_WIFI_P2P;
-import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+import static android.net.INetd.IF_STATE_UP;
+import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHERING_WIFI_P2P;
+import static android.net.TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
 import static android.net.ip.IpServer.STATE_AVAILABLE;
 import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
@@ -52,7 +53,7 @@
 
 import android.net.INetd;
 import android.net.INetworkStatsService;
-import android.net.InterfaceConfiguration;
+import android.net.InterfaceConfigurationParcel;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -64,7 +65,6 @@
 import android.net.util.InterfaceParams;
 import android.net.util.InterfaceSet;
 import android.net.util.SharedLog;
-import android.os.INetworkManagementService;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
 import android.text.TextUtils;
@@ -89,6 +89,8 @@
     private static final String IFACE_NAME = "testnet1";
     private static final String UPSTREAM_IFACE = "upstream0";
     private static final String UPSTREAM_IFACE2 = "upstream1";
+    private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
+    private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
     private static final int DHCP_LEASE_TIME_SECS = 3600;
 
     private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
@@ -96,11 +98,9 @@
 
     private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
 
-    @Mock private INetworkManagementService mNMService;
     @Mock private INetd mNetd;
     @Mock private INetworkStatsService mStatsService;
     @Mock private IpServer.Callback mCallback;
-    @Mock private InterfaceConfiguration mInterfaceConfiguration;
     @Mock private SharedLog mSharedLog;
     @Mock private IDhcpServer mDhcpServer;
     @Mock private RouterAdvertisementDaemon mRaDaemon;
@@ -112,6 +112,7 @@
     private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
             ArgumentCaptor.forClass(LinkProperties.class);
     private IpServer mIpServer;
+    private InterfaceConfigurationParcel mInterfaceConfiguration;
 
     private void initStateMachine(int interfaceType) throws Exception {
         initStateMachine(interfaceType, false /* usingLegacyDhcp */);
@@ -131,17 +132,20 @@
         }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
         when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
         when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
-        when(mDependencies.getNetdService()).thenReturn(mNetd);
-
+        mInterfaceConfiguration = new InterfaceConfigurationParcel();
+        mInterfaceConfiguration.flags = new String[0];
+        if (interfaceType == TETHERING_BLUETOOTH) {
+            mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR;
+            mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH;
+        }
         mIpServer = new IpServer(
-                IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
-                mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies);
+                IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mStatsService,
+                mCallback, usingLegacyDhcp, mDependencies);
         mIpServer.start();
         // Starting the state machine always puts us in a consistent state and notifies
         // the rest of the world that we've changed from an unknown to available state.
         mLooper.dispatchAll();
-        reset(mNMService, mStatsService, mCallback);
-        when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
+        reset(mNetd, mStatsService, mCallback);
 
         when(mRaDaemon.start()).thenReturn(true);
     }
@@ -158,8 +162,7 @@
         if (upstreamIface != null) {
             dispatchTetherConnectionChanged(upstreamIface);
         }
-        reset(mNMService, mStatsService, mCallback);
-        when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
+        reset(mNetd, mStatsService, mCallback);
     }
 
     @Before public void setUp() throws Exception {
@@ -169,15 +172,14 @@
 
     @Test
     public void startsOutAvailable() {
-        mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(),
-                TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mCallback,
-                false /* usingLegacyDhcp */, mDependencies);
+        mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
+                mNetd, mStatsService, mCallback, false /* usingLegacyDhcp */, mDependencies);
         mIpServer.start();
         mLooper.dispatchAll();
         verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
         verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mCallback, mNMService, mStatsService);
+        verifyNoMoreInteractions(mCallback, mNetd, mStatsService);
     }
 
     @Test
@@ -196,7 +198,7 @@
             // None of these commands should trigger us to request action from
             // the rest of the system.
             dispatchCommand(command);
-            verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
+            verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
         }
     }
 
@@ -208,7 +210,7 @@
         verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
         verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
     }
 
     @Test
@@ -216,13 +218,17 @@
         initStateMachine(TETHERING_BLUETOOTH);
 
         dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
-        InOrder inOrder = inOrder(mCallback, mNMService);
-        inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
+        InOrder inOrder = inOrder(mCallback, mNetd);
+        inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
+        inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
+        // One for ipv4 route, one for ipv6 link local route.
+        inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
+                any(), any());
         inOrder.verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
     }
 
     @Test
@@ -230,14 +236,16 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, null);
 
         dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
-        InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback);
-        inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
+        InOrder inOrder = inOrder(mNetd, mStatsService, mCallback);
+        inOrder.verify(mNetd).tetherApplyDnsInterfaces();
+        inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
+        inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
         inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
         inOrder.verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
     }
 
     @Test
@@ -245,16 +253,19 @@
         initStateMachine(TETHERING_USB);
 
         dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
-        InOrder inOrder = inOrder(mCallback, mNMService);
-        inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
-        inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
-        inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
+        InOrder inOrder = inOrder(mCallback, mNetd);
+        inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
+                  IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
+        inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
+        inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
+        inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
+                any(), any());
         inOrder.verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), mLinkPropertiesCaptor.capture());
         assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
-        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
     }
 
     @Test
@@ -262,16 +273,19 @@
         initStateMachine(TETHERING_WIFI_P2P);
 
         dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
-        InOrder inOrder = inOrder(mCallback, mNMService);
-        inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
-        inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
-        inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
+        InOrder inOrder = inOrder(mCallback, mNetd);
+        inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
+                  IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
+        inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
+        inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
+        inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
+                any(), any());
         inOrder.verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), mLinkPropertiesCaptor.capture());
         assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
-        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
     }
 
     @Test
@@ -281,10 +295,10 @@
         // Telling the state machine about its upstream interface triggers
         // a little more configuration.
         dispatchTetherConnectionChanged(UPSTREAM_IFACE);
-        InOrder inOrder = inOrder(mNMService);
-        inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE);
-        inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
-        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
+        InOrder inOrder = inOrder(mNetd);
+        inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
+        inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
+        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
     }
 
     @Test
@@ -292,49 +306,49 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
 
         dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
-        InOrder inOrder = inOrder(mNMService, mStatsService);
+        InOrder inOrder = inOrder(mNetd, mStatsService);
         inOrder.verify(mStatsService).forceUpdate();
-        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
-        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
-        inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
-        inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
-        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
+        inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
+        inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
+        inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
+        inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
+        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
     }
 
     @Test
     public void handlesChangingUpstreamNatFailure() throws Exception {
         initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
 
-        doThrow(RemoteException.class).when(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
+        doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
 
         dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
-        InOrder inOrder = inOrder(mNMService, mStatsService);
+        InOrder inOrder = inOrder(mNetd, mStatsService);
         inOrder.verify(mStatsService).forceUpdate();
-        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
-        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
-        inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
+        inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
+        inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
+        inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
         inOrder.verify(mStatsService).forceUpdate();
-        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
-        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2);
+        inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
+        inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
     }
 
     @Test
     public void handlesChangingUpstreamInterfaceForwardingFailure() throws Exception {
         initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
 
-        doThrow(RemoteException.class).when(mNMService).startInterfaceForwarding(
+        doThrow(RemoteException.class).when(mNetd).ipfwdAddInterfaceForward(
                 IFACE_NAME, UPSTREAM_IFACE2);
 
         dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
-        InOrder inOrder = inOrder(mNMService, mStatsService);
+        InOrder inOrder = inOrder(mNetd, mStatsService);
         inOrder.verify(mStatsService).forceUpdate();
-        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
-        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
-        inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
-        inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
+        inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
+        inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
+        inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
+        inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
         inOrder.verify(mStatsService).forceUpdate();
-        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
-        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2);
+        inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
+        inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
     }
 
     @Test
@@ -342,17 +356,19 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
 
         dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
-        InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback);
+        InOrder inOrder = inOrder(mNetd, mStatsService, mCallback);
         inOrder.verify(mStatsService).forceUpdate();
-        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
-        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
-        inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
+        inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
+        inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
+        inOrder.verify(mNetd).tetherApplyDnsInterfaces();
+        inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
+        inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
         inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
         inOrder.verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
     }
 
     @Test
@@ -361,13 +377,14 @@
             initTetheredStateMachine(TETHERING_USB, null);
 
             if (shouldThrow) {
-                doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME);
+                doThrow(RemoteException.class).when(mNetd).tetherInterfaceRemove(IFACE_NAME);
             }
             dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
-            InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
-            usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
-            usbTeardownOrder.verify(mNMService).setInterfaceConfig(
-                    IFACE_NAME, mInterfaceConfiguration);
+            InOrder usbTeardownOrder = inOrder(mNetd, mCallback);
+            // Currently IpServer interfaceSetCfg twice to stop IPv4. One just set interface down
+            // Another one is set IPv4 to 0.0.0.0/0 as clearng ipv4 address.
+            usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
+                    argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
             usbTeardownOrder.verify(mCallback).updateInterfaceState(
                     mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
             usbTeardownOrder.verify(mCallback).updateLinkProperties(
@@ -380,12 +397,15 @@
     public void usbShouldBeTornDownOnTetherError() throws Exception {
         initStateMachine(TETHERING_USB);
 
-        doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME);
+        doThrow(RemoteException.class).when(mNetd).tetherInterfaceAdd(IFACE_NAME);
         dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
-        InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
-        usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
-        usbTeardownOrder.verify(mNMService).setInterfaceConfig(
-                IFACE_NAME, mInterfaceConfiguration);
+        InOrder usbTeardownOrder = inOrder(mNetd, mCallback);
+        usbTeardownOrder.verify(mNetd).interfaceSetCfg(
+                argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
+        usbTeardownOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
+
+        usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
+                argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
         usbTeardownOrder.verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
         usbTeardownOrder.verify(mCallback).updateLinkProperties(
@@ -397,11 +417,13 @@
     public void shouldTearDownUsbOnUpstreamError() throws Exception {
         initTetheredStateMachine(TETHERING_USB, null);
 
-        doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString());
+        doThrow(RemoteException.class).when(mNetd).tetherAddForward(anyString(), anyString());
         dispatchTetherConnectionChanged(UPSTREAM_IFACE);
-        InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
-        usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
-        usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
+        InOrder usbTeardownOrder = inOrder(mNetd, mCallback);
+        usbTeardownOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
+
+        usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
+                argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
         usbTeardownOrder.verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
         usbTeardownOrder.verify(mCallback).updateLinkProperties(
@@ -413,11 +435,11 @@
     public void ignoresDuplicateUpstreamNotifications() throws Exception {
         initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
 
-        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
 
         for (int i = 0; i < 5; i++) {
             dispatchTetherConnectionChanged(UPSTREAM_IFACE);
-            verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
+            verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
         }
     }
 
@@ -525,4 +547,12 @@
         // never see an empty interface name in any LinkProperties update.
         assertFalse(TextUtils.isEmpty(lp.getInterfaceName()));
     }
+
+    private boolean assertContainsFlag(String[] flags, String match) {
+        for (String flag : flags) {
+            if (flag.equals(match)) return true;
+        }
+        fail("Missing flag: " + match);
+        return false;
+    }
 }
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 99cf9e9..66eba9a 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -16,12 +16,12 @@
 
 package com.android.server.connectivity.tethering;
 
-import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
-import static android.net.ConnectivityManager.TETHERING_USB;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
+import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 04b2eb4..7af48a8 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -19,16 +19,15 @@
 import static android.hardware.usb.UsbManager.USB_CONFIGURED;
 import static android.hardware.usb.UsbManager.USB_CONNECTED;
 import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
-import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
-import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
-import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
-import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
-import static android.net.ConnectivityManager.TETHERING_USB;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
 import static android.net.RouteInfo.RTN_UNICAST;
+import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
+import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
+import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER;
+import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER;
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
@@ -50,7 +49,6 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -74,14 +72,13 @@
 import android.net.INetworkStatsService;
 import android.net.ITetheringEventCallback;
 import android.net.InetAddresses;
-import android.net.InterfaceConfiguration;
+import android.net.InterfaceConfigurationParcel;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.MacAddress;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
 import android.net.NetworkRequest;
 import android.net.RouteInfo;
 import android.net.TetherStatesParcel;
@@ -147,6 +144,7 @@
     private static final String TEST_USB_IFNAME = "test_rndis0";
     private static final String TEST_WLAN_IFNAME = "test_wlan0";
     private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
+    private static final String TETHERING_NAME = "Tethering";
 
     private static final int DHCPSERVER_START_TIMEOUT_MS = 1000;
 
@@ -185,6 +183,7 @@
     private BroadcastReceiver mBroadcastReceiver;
     private Tethering mTethering;
     private PhoneStateListener mPhoneStateListener;
+    private InterfaceConfigurationParcel mInterfaceConfiguration;
 
     private class TestContext extends BroadcastInterceptingContext {
         TestContext(Context base) {
@@ -248,11 +247,6 @@
         }
 
         @Override
-        public INetd getNetdService() {
-            return mNetd;
-        }
-
-        @Override
         public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
                 DhcpServerCallbacks cb) {
             new Thread(() -> {
@@ -429,11 +423,11 @@
                 .thenReturn(new int[0]);
         when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
                 .thenReturn(false);
-        when(mNMService.listInterfaces())
+        when(mNetd.interfaceGetList())
                 .thenReturn(new String[] {
                         TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME});
-        when(mNMService.getInterfaceConfig(anyString()))
-                .thenReturn(new InterfaceConfiguration());
+        mInterfaceConfiguration = new InterfaceConfigurationParcel();
+        mInterfaceConfiguration.flags = new String[0];
         when(mRouterAdvertisementDaemon.start())
                 .thenReturn(true);
 
@@ -495,15 +489,12 @@
         p2pInfo.groupFormed = isGroupFormed;
         p2pInfo.isGroupOwner = isGroupOwner;
 
-        NetworkInfo networkInfo = new NetworkInfo(TYPE_WIFI_P2P, 0, null, null);
-
         WifiP2pGroup group = new WifiP2pGroup();
         group.setIsGroupOwner(isGroupOwner);
         group.setInterface(ifname);
 
         final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
         intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, p2pInfo);
-        intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, networkInfo);
         intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, group);
         mServiceContext.sendBroadcastAsUserMultiplePermissions(intent, UserHandle.ALL,
                 P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST);
@@ -523,10 +514,11 @@
     }
 
     private void verifyInterfaceServingModeStarted(String ifname) throws Exception {
-        verify(mNMService, times(1)).getInterfaceConfig(ifname);
-        verify(mNMService, times(1))
-                .setInterfaceConfig(eq(ifname), any(InterfaceConfiguration.class));
-        verify(mNMService, times(1)).tetherInterface(ifname);
+        verify(mNetd, times(1)).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
+        verify(mNetd, times(1)).tetherInterfaceAdd(ifname);
+        verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, ifname);
+        verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(ifname),
+                anyString(), anyString());
     }
 
     private void verifyTetheringBroadcast(String ifname, String whichExtra) {
@@ -558,7 +550,7 @@
             verify(mWifiManager).updateInterfaceIpState(
                     TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
         }
-        verifyNoMoreInteractions(mNMService);
+        verifyNoMoreInteractions(mNetd);
         verifyNoMoreInteractions(mWifiManager);
     }
 
@@ -581,14 +573,14 @@
         prepareUsbTethering(upstreamState);
 
         // This should produce no activity of any kind.
-        verifyNoMoreInteractions(mNMService);
+        verifyNoMoreInteractions(mNetd);
 
         // Pretend we then receive USB configured broadcast.
         sendUsbBroadcast(true, true, true);
         mLooper.dispatchAll();
         // Now we should see the start of tethering mechanics (in this case:
         // tetherMatchingInterfaces() which starts by fetching all interfaces).
-        verify(mNMService, times(1)).listInterfaces();
+        verify(mNetd, times(1)).interfaceGetList();
 
         // UpstreamNetworkMonitor should receive selected upstream
         verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any());
@@ -618,9 +610,9 @@
 
         verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
-        verify(mNMService, times(1)).setIpForwardingEnabled(true);
-        verify(mNMService, times(1)).startTethering(any(String[].class));
-        verifyNoMoreInteractions(mNMService);
+        verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
+        verify(mNetd, times(1)).tetherStartWithConfiguration(any());
+        verifyNoMoreInteractions(mNetd);
         verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
         verify(mWifiManager).updateInterfaceIpState(
@@ -638,16 +630,16 @@
         mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
         mLooper.dispatchAll();
 
-        verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
-        // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4.
-        verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
-        verify(mNMService, times(2))
-                .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
-        verify(mNMService, times(1)).stopTethering();
-        verify(mNMService, times(1)).setIpForwardingEnabled(false);
+        verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+        verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME);
+        verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
+        // interfaceSetCfg() called once for enabling and twice disabling IPv4.
+        verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
+        verify(mNetd, times(1)).tetherStop();
+        verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME);
         verify(mWifiManager, times(3)).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
-        verifyNoMoreInteractions(mNMService);
+        verifyNoMoreInteractions(mNetd);
         verifyNoMoreInteractions(mWifiManager);
         // Asking for the last error after the per-interface state machine
         // has been reaped yields an unknown interface error.
@@ -684,8 +676,8 @@
         UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
         runUsbTethering(upstreamState);
 
-        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
-        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
 
         sendIPv6TetherUpdates(upstreamState);
         verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
@@ -708,8 +700,8 @@
         UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
         runUsbTethering(upstreamState);
 
-        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
-        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
 
         sendIPv6TetherUpdates(upstreamState);
         verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
@@ -721,8 +713,8 @@
         UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
         runUsbTethering(upstreamState);
 
-        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
-        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mRouterAdvertisementDaemon, times(1)).start();
         verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
 
@@ -736,12 +728,11 @@
         UpstreamNetworkState upstreamState = buildMobile464xlatUpstreamState();
         runUsbTethering(upstreamState);
 
-        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
-        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+        verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
-        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
-        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
-                TEST_XLAT_MOBILE_IFNAME);
+        verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+        verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
 
         sendIPv6TetherUpdates(upstreamState);
         verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
@@ -754,9 +745,9 @@
         UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
         runUsbTethering(upstreamState);
 
-        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
-        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
 
         // Then 464xlat comes up
         upstreamState = buildMobile464xlatUpstreamState();
@@ -772,12 +763,11 @@
         mLooper.dispatchAll();
 
         // Forwarding is added for 464xlat
-        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
-        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
-                TEST_XLAT_MOBILE_IFNAME);
+        verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+        verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
         // Forwarding was not re-added for v6 (still times(1))
-        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
-        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         // DHCP not restarted on downstream (still times(1))
         verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
     }
@@ -820,7 +810,7 @@
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).startTetheredHotspot(null);
         verifyNoMoreInteractions(mWifiManager);
-        verifyNoMoreInteractions(mNMService);
+        verifyNoMoreInteractions(mNetd);
 
         // Emulate externally-visible WifiManager effects, causing the
         // per-interface state machine to start up, and telling us that
@@ -833,7 +823,7 @@
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
         verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
-        verifyNoMoreInteractions(mNMService);
+        verifyNoMoreInteractions(mNetd);
         verifyNoMoreInteractions(mWifiManager);
     }
 
@@ -847,7 +837,7 @@
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).startTetheredHotspot(null);
         verifyNoMoreInteractions(mWifiManager);
-        verifyNoMoreInteractions(mNMService);
+        verifyNoMoreInteractions(mNetd);
 
         // Emulate externally-visible WifiManager effects, causing the
         // per-interface state machine to start up, and telling us that
@@ -858,9 +848,11 @@
 
         verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
-        verify(mNMService, times(1)).setIpForwardingEnabled(true);
-        verify(mNMService, times(1)).startTethering(any(String[].class));
-        verifyNoMoreInteractions(mNMService);
+        verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
+        verify(mNetd, times(1)).tetherStartWithConfiguration(any());
+        verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME),
+                anyString(), anyString());
+        verifyNoMoreInteractions(mNetd);
         verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
         verify(mWifiManager).updateInterfaceIpState(
@@ -878,8 +870,8 @@
         /////
         // We do not currently emulate any upstream being found.
         //
-        // This is why there are no calls to verify mNMService.enableNat() or
-        // mNMService.startInterfaceForwarding().
+        // This is why there are no calls to verify mNetd.tetherAddForward() or
+        // mNetd.ipfwdAddInterfaceForward().
         /////
 
         // Emulate pressing the WiFi tethering button.
@@ -887,7 +879,7 @@
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).stopSoftAp();
         verifyNoMoreInteractions(mWifiManager);
-        verifyNoMoreInteractions(mNMService);
+        verifyNoMoreInteractions(mNetd);
 
         // Emulate externally-visible WifiManager effects, when tethering mode
         // is being torn down.
@@ -895,16 +887,16 @@
         mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
         mLooper.dispatchAll();
 
-        verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
-        // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4.
-        verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
-        verify(mNMService, atLeastOnce())
-                .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
-        verify(mNMService, times(1)).stopTethering();
-        verify(mNMService, times(1)).setIpForwardingEnabled(false);
+        verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+        verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME);
+        verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
+        // interfaceSetCfg() called once for enabling and twice for disabling IPv4.
+        verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
+        verify(mNetd, times(1)).tetherStop();
+        verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME);
         verify(mWifiManager, times(3)).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
-        verifyNoMoreInteractions(mNMService);
+        verifyNoMoreInteractions(mNetd);
         verifyNoMoreInteractions(mWifiManager);
         // Asking for the last error after the per-interface state machine
         // has been reaped yields an unknown interface error.
@@ -915,14 +907,14 @@
     @Test
     public void failureEnablingIpForwarding() throws Exception {
         when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
-        doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true);
+        doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
 
         // Emulate pressing the WiFi tethering button.
         mTethering.startTethering(TETHERING_WIFI, null, false);
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).startTetheredHotspot(null);
         verifyNoMoreInteractions(mWifiManager);
-        verifyNoMoreInteractions(mNMService);
+        verifyNoMoreInteractions(mNetd);
 
         // Emulate externally-visible WifiManager effects, causing the
         // per-interface state machine to start up, and telling us that
@@ -931,15 +923,15 @@
         sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
         mLooper.dispatchAll();
 
-        // We verify get/set called thrice here: twice for setup (on NMService) and once during
-        // teardown (on Netd) because all events happen over the course of the single
+        // We verify get/set called three times here: twice for setup and once during
+        // teardown because all events happen over the course of the single
         // dispatchAll() above. Note that once the IpServer IPv4 address config
         // code is refactored the two calls during shutdown will revert to one.
-        verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
-        verify(mNMService, times(2))
-                .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
-        verify(mNetd, times(1)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName)));
-        verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
+        verify(mNetd, times(3)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName)));
+        verify(mNetd, times(1)).tetherInterfaceAdd(TEST_WLAN_IFNAME);
+        verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
+        verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME),
+                anyString(), anyString());
         verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
         verify(mWifiManager).updateInterfaceIpState(
@@ -949,18 +941,20 @@
         assertEquals(3, mTetheringDependencies.mIsTetheringSupportedCalls);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
         // This is called, but will throw.
-        verify(mNMService, times(1)).setIpForwardingEnabled(true);
+        verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
         // This never gets called because of the exception thrown above.
-        verify(mNMService, times(0)).startTethering(any(String[].class));
+        verify(mNetd, times(0)).tetherStartWithConfiguration(any());
         // When the master state machine transitions to an error state it tells
         // downstream interfaces, which causes us to tell Wi-Fi about the error
         // so it can take down AP mode.
-        verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
+        verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+        verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME);
+        verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
         verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
 
         verifyNoMoreInteractions(mWifiManager);
-        verifyNoMoreInteractions(mNMService);
+        verifyNoMoreInteractions(mNetd);
     }
 
     private void runUserRestrictionsChange(
@@ -1232,9 +1226,9 @@
 
         verifyInterfaceServingModeStarted(TEST_P2P_IFNAME);
         verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER);
-        verify(mNMService, times(1)).setIpForwardingEnabled(true);
-        verify(mNMService, times(1)).startTethering(any(String[].class));
-        verifyNoMoreInteractions(mNMService);
+        verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
+        verify(mNetd, times(1)).tetherStartWithConfiguration(any());
+        verifyNoMoreInteractions(mNetd);
         verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
         verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
         // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
@@ -1249,16 +1243,16 @@
         mTethering.interfaceRemoved(TEST_P2P_IFNAME);
         mLooper.dispatchAll();
 
-        verify(mNMService, times(1)).untetherInterface(TEST_P2P_IFNAME);
-        // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4.
-        verify(mNMService, times(2)).getInterfaceConfig(TEST_P2P_IFNAME);
-        verify(mNMService, times(2))
-                .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class));
-        verify(mNMService, times(1)).stopTethering();
-        verify(mNMService, times(1)).setIpForwardingEnabled(false);
+        verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+        verify(mNetd, times(1)).tetherInterfaceRemove(TEST_P2P_IFNAME);
+        verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME);
+        // interfaceSetCfg() called once for enabling and twice for disabling IPv4.
+        verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
+        verify(mNetd, times(1)).tetherStop();
+        verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME);
         verify(mUpstreamNetworkMonitor, never()).getCurrentPreferredUpstream();
         verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any());
-        verifyNoMoreInteractions(mNMService);
+        verifyNoMoreInteractions(mNetd);
         // Asking for the last error after the per-interface state machine
         // has been reaped yields an unknown interface error.
         assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
@@ -1272,12 +1266,11 @@
         sendWifiP2pConnectionChanged(true, false, TEST_P2P_IFNAME);
         mLooper.dispatchAll();
 
-        verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME);
-        verify(mNMService, never())
-                .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class));
-        verify(mNMService, never()).tetherInterface(TEST_P2P_IFNAME);
-        verify(mNMService, never()).setIpForwardingEnabled(true);
-        verify(mNMService, never()).startTethering(any(String[].class));
+        verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
+        verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME);
+        verify(mNetd, never()).networkAddInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME);
+        verify(mNetd, never()).ipfwdEnableForwarding(TETHERING_NAME);
+        verify(mNetd, never()).tetherStartWithConfiguration(any());
 
         // Emulate externally-visible WifiP2pManager effects, when wifi p2p group
         // is being removed.
@@ -1285,13 +1278,13 @@
         mTethering.interfaceRemoved(TEST_P2P_IFNAME);
         mLooper.dispatchAll();
 
-        verify(mNMService, never()).untetherInterface(TEST_P2P_IFNAME);
-        verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME);
-        verify(mNMService, never())
-                .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class));
-        verify(mNMService, never()).stopTethering();
-        verify(mNMService, never()).setIpForwardingEnabled(false);
-        verifyNoMoreInteractions(mNMService);
+        verify(mNetd, never()).tetherApplyDnsInterfaces();
+        verify(mNetd, never()).tetherInterfaceRemove(TEST_P2P_IFNAME);
+        verify(mNetd, never()).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME);
+        verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
+        verify(mNetd, never()).tetherStop();
+        verify(mNetd, never()).ipfwdDisableForwarding(TETHERING_NAME);
+        verifyNoMoreInteractions(mNetd);
         // Asking for the last error after the per-interface state machine
         // has been reaped yields an unknown interface error.
         assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
@@ -1321,12 +1314,11 @@
         sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME);
         mLooper.dispatchAll();
 
-        verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME);
-        verify(mNMService, never())
-                .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class));
-        verify(mNMService, never()).tetherInterface(TEST_P2P_IFNAME);
-        verify(mNMService, never()).setIpForwardingEnabled(true);
-        verify(mNMService, never()).startTethering(any(String[].class));
+        verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
+        verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME);
+        verify(mNetd, never()).networkAddInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME);
+        verify(mNetd, never()).ipfwdEnableForwarding(TETHERING_NAME);
+        verify(mNetd, never()).tetherStartWithConfiguration(any());
         assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
     }
     @Test
diff --git a/rs/java/android/renderscript/BaseObj.java b/rs/java/android/renderscript/BaseObj.java
index b7e05d9..7b5514b 100644
--- a/rs/java/android/renderscript/BaseObj.java
+++ b/rs/java/android/renderscript/BaseObj.java
@@ -16,8 +16,10 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import dalvik.system.CloseGuard;
+
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 /**
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index b8eb3a1..0941907 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -16,7 +16,7 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * <p>An Element represents one item within an {@link
diff --git a/rs/java/android/renderscript/FileA3D.java b/rs/java/android/renderscript/FileA3D.java
index 9a6b0bc..7cc2825 100644
--- a/rs/java/android/renderscript/FileA3D.java
+++ b/rs/java/android/renderscript/FileA3D.java
@@ -16,13 +16,13 @@
 
 package android.renderscript;
 
-import java.io.File;
-import java.io.InputStream;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 
+import java.io.File;
+import java.io.InputStream;
+
 /**
  * @hide
  * @deprecated in API 16
diff --git a/rs/java/android/renderscript/Font.java b/rs/java/android/renderscript/Font.java
index 583350e..df9d801 100644
--- a/rs/java/android/renderscript/Font.java
+++ b/rs/java/android/renderscript/Font.java
@@ -16,17 +16,16 @@
 
 package android.renderscript;
 
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.os.Environment;
+
 import java.io.File;
 import java.io.InputStream;
 import java.util.HashMap;
 import java.util.Map;
 
-import android.os.Environment;
-
-import android.annotation.UnsupportedAppUsage;
-import android.content.res.AssetManager;
-import android.content.res.Resources;
-
 /**
  * @hide
  * @deprecated in API 16
diff --git a/rs/java/android/renderscript/Matrix4f.java b/rs/java/android/renderscript/Matrix4f.java
index 026c9fb..a9469c9 100644
--- a/rs/java/android/renderscript/Matrix4f.java
+++ b/rs/java/android/renderscript/Matrix4f.java
@@ -16,8 +16,7 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
-import java.lang.Math;
+import android.compat.annotation.UnsupportedAppUsage;
 
 
 /**
diff --git a/rs/java/android/renderscript/Mesh.java b/rs/java/android/renderscript/Mesh.java
index 5321dcb..826225a 100644
--- a/rs/java/android/renderscript/Mesh.java
+++ b/rs/java/android/renderscript/Mesh.java
@@ -16,7 +16,8 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.util.Vector;
 
 /**
diff --git a/rs/java/android/renderscript/Program.java b/rs/java/android/renderscript/Program.java
index e28d646..ff07218 100644
--- a/rs/java/android/renderscript/Program.java
+++ b/rs/java/android/renderscript/Program.java
@@ -17,14 +17,14 @@
 package android.renderscript;
 
 
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.Resources;
+import android.util.Log;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 
-import android.annotation.UnsupportedAppUsage;
-import android.content.res.Resources;
-import android.util.Log;
-
 
 /**
  * @hide
diff --git a/rs/java/android/renderscript/ProgramFragment.java b/rs/java/android/renderscript/ProgramFragment.java
index 3dde9b6..8805312 100644
--- a/rs/java/android/renderscript/ProgramFragment.java
+++ b/rs/java/android/renderscript/ProgramFragment.java
@@ -16,7 +16,7 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 
 /**
diff --git a/rs/java/android/renderscript/ProgramFragmentFixedFunction.java b/rs/java/android/renderscript/ProgramFragmentFixedFunction.java
index d05d41d..c741ce6 100644
--- a/rs/java/android/renderscript/ProgramFragmentFixedFunction.java
+++ b/rs/java/android/renderscript/ProgramFragmentFixedFunction.java
@@ -16,7 +16,7 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 
 /**
diff --git a/rs/java/android/renderscript/ProgramRaster.java b/rs/java/android/renderscript/ProgramRaster.java
index 33000ac..a21696c 100644
--- a/rs/java/android/renderscript/ProgramRaster.java
+++ b/rs/java/android/renderscript/ProgramRaster.java
@@ -16,7 +16,7 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 
 /**
diff --git a/rs/java/android/renderscript/ProgramStore.java b/rs/java/android/renderscript/ProgramStore.java
index 622fe21..7e61347 100644
--- a/rs/java/android/renderscript/ProgramStore.java
+++ b/rs/java/android/renderscript/ProgramStore.java
@@ -16,7 +16,7 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 
 /**
diff --git a/rs/java/android/renderscript/ProgramVertex.java b/rs/java/android/renderscript/ProgramVertex.java
index 83d9ea7..9257234 100644
--- a/rs/java/android/renderscript/ProgramVertex.java
+++ b/rs/java/android/renderscript/ProgramVertex.java
@@ -38,7 +38,7 @@
  **/
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 
 /**
diff --git a/rs/java/android/renderscript/ProgramVertexFixedFunction.java b/rs/java/android/renderscript/ProgramVertexFixedFunction.java
index 579d3bb..03c2eaf 100644
--- a/rs/java/android/renderscript/ProgramVertexFixedFunction.java
+++ b/rs/java/android/renderscript/ProgramVertexFixedFunction.java
@@ -16,7 +16,7 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 
 /**
diff --git a/rs/java/android/renderscript/RSSurfaceView.java b/rs/java/android/renderscript/RSSurfaceView.java
index 561373c..6bdde38 100644
--- a/rs/java/android/renderscript/RSSurfaceView.java
+++ b/rs/java/android/renderscript/RSSurfaceView.java
@@ -16,7 +16,7 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.SurfaceHolder;
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index f4c2777..39efe73 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -16,7 +16,7 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.AssetManager;
 import android.graphics.Bitmap;
@@ -447,44 +447,33 @@
         validate();
         return rsnAllocationCreateTyped(mContext, type, mip, usage, pointer);
     }
-    native long rsnAllocationCreateFromBitmap(long con, long type, int mip, long bitmapHandle,
+
+    native long rsnAllocationCreateFromBitmap(long con, long type, int mip, Bitmap bmp,
                 int usage);
     synchronized long nAllocationCreateFromBitmap(long type, int mip, Bitmap bmp, int usage) {
         validate();
-        return rsnAllocationCreateFromBitmap(mContext, type, mip, bmp.getNativeInstance(), usage);
+        return rsnAllocationCreateFromBitmap(mContext, type, mip, bmp, usage);
     }
 
-    native long rsnAllocationCreateBitmapBackedAllocation(long con, long type, int mip, long bitmapHandle,
+    native long rsnAllocationCreateBitmapBackedAllocation(long con, long type, int mip, Bitmap bmp,
                 int usage);
     synchronized long nAllocationCreateBitmapBackedAllocation(long type, int mip, Bitmap bmp,
                 int usage) {
         validate();
-        return rsnAllocationCreateBitmapBackedAllocation(mContext, type, mip, bmp.getNativeInstance(),
-                usage);
+        return rsnAllocationCreateBitmapBackedAllocation(mContext, type, mip, bmp, usage);
     }
 
-    native long rsnAllocationCubeCreateFromBitmap(long con, long type, int mip, long bitmapHandle,
+    native long rsnAllocationCubeCreateFromBitmap(long con, long type, int mip, Bitmap bmp,
                 int usage);
     synchronized long nAllocationCubeCreateFromBitmap(long type, int mip, Bitmap bmp, int usage) {
         validate();
-        return rsnAllocationCubeCreateFromBitmap(mContext, type, mip, bmp.getNativeInstance(),
-                usage);
-    }
-    native long  rsnAllocationCreateBitmapRef(long con, long type, long bitmapHandle);
-    synchronized long nAllocationCreateBitmapRef(long type, Bitmap bmp) {
-        validate();
-        return rsnAllocationCreateBitmapRef(mContext, type, bmp.getNativeInstance());
-    }
-    native long  rsnAllocationCreateFromAssetStream(long con, int mips, int assetStream, int usage);
-    synchronized long nAllocationCreateFromAssetStream(int mips, int assetStream, int usage) {
-        validate();
-        return rsnAllocationCreateFromAssetStream(mContext, mips, assetStream, usage);
+        return rsnAllocationCubeCreateFromBitmap(mContext, type, mip, bmp, usage);
     }
 
-    native void  rsnAllocationCopyToBitmap(long con, long alloc, long bitmapHandle);
+    native void  rsnAllocationCopyToBitmap(long con, long alloc, Bitmap bmp);
     synchronized void nAllocationCopyToBitmap(long alloc, Bitmap bmp) {
         validate();
-        rsnAllocationCopyToBitmap(mContext, alloc, bmp.getNativeInstance());
+        rsnAllocationCopyToBitmap(mContext, alloc, bmp);
     }
 
     native void rsnAllocationSyncAll(long con, long alloc, int src);
@@ -537,10 +526,10 @@
         validate();
         rsnAllocationGenerateMipmaps(mContext, alloc);
     }
-    native void  rsnAllocationCopyFromBitmap(long con, long alloc, long bitmapHandle);
+    native void  rsnAllocationCopyFromBitmap(long con, long alloc, Bitmap bmp);
     synchronized void nAllocationCopyFromBitmap(long alloc, Bitmap bmp) {
         validate();
-        rsnAllocationCopyFromBitmap(mContext, alloc, bmp.getNativeInstance());
+        rsnAllocationCopyFromBitmap(mContext, alloc, bmp);
     }
 
 
diff --git a/rs/java/android/renderscript/RenderScriptCacheDir.java b/rs/java/android/renderscript/RenderScriptCacheDir.java
index 1797bef..862d032 100644
--- a/rs/java/android/renderscript/RenderScriptCacheDir.java
+++ b/rs/java/android/renderscript/RenderScriptCacheDir.java
@@ -16,7 +16,8 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.io.File;
 
 /**
diff --git a/rs/java/android/renderscript/RenderScriptGL.java b/rs/java/android/renderscript/RenderScriptGL.java
index 6fac83e..dafaf36 100644
--- a/rs/java/android/renderscript/RenderScriptGL.java
+++ b/rs/java/android/renderscript/RenderScriptGL.java
@@ -16,7 +16,7 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.SurfaceTexture;
 import android.view.Surface;
diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java
index 9ad9aea..d1d3a76 100644
--- a/rs/java/android/renderscript/Script.java
+++ b/rs/java/android/renderscript/Script.java
@@ -16,7 +16,7 @@
 
 package android.renderscript;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.util.SparseArray;
 
 /**
diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk
index 0854b95..f9ef0b7 100644
--- a/rs/jni/Android.mk
+++ b/rs/jni/Android.mk
@@ -12,7 +12,6 @@
     libRS \
     libcutils \
     liblog \
-    libhwui \
     libutils \
     libui \
     libgui \
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index dfee961..5ae895d 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -32,10 +32,10 @@
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
+#include <android/graphics/bitmap.h>
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/android_view_Surface.h"
 #include "android_runtime/android_util_AssetManager.h"
-#include "android/graphics/GraphicsJNI.h"
 #include "android/native_window.h"
 #include "android/native_window_jni.h"
 
@@ -1319,27 +1319,28 @@
     rsAllocationGenerateMipmaps((RsContext)con, (RsAllocation)alloc);
 }
 
+static size_t computeByteSize(const android::graphics::Bitmap& bitmap) {
+    AndroidBitmapInfo info = bitmap.getInfo();
+    return info.height * info.stride;
+}
+
 static jlong
 nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type, jint mip,
-                            jlong bitmapPtr, jint usage)
+                            jobject jbitmap, jint usage)
 {
-    SkBitmap bitmap;
-    bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
-
+    android::graphics::Bitmap bitmap(_env, jbitmap);
     const void* ptr = bitmap.getPixels();
     jlong id = (jlong)(uintptr_t)rsAllocationCreateFromBitmap((RsContext)con,
                                                   (RsType)type, (RsAllocationMipmapControl)mip,
-                                                  ptr, bitmap.computeByteSize(), usage);
+                                                  ptr, computeByteSize(bitmap), usage);
     return id;
 }
 
 static jlong
 nAllocationCreateBitmapBackedAllocation(JNIEnv *_env, jobject _this, jlong con, jlong type,
-                                        jint mip, jlong bitmapPtr, jint usage)
+                                        jint mip, jobject jbitmap, jint usage)
 {
-    SkBitmap bitmap;
-    bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
-
+    android::graphics::Bitmap bitmap(_env, jbitmap);
     const void* ptr = bitmap.getPixels();
     jlong id = (jlong)(uintptr_t)rsAllocationCreateTyped((RsContext)con,
                                             (RsType)type, (RsAllocationMipmapControl)mip,
@@ -1349,40 +1350,35 @@
 
 static jlong
 nAllocationCubeCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type, jint mip,
-                                jlong bitmapPtr, jint usage)
+                                jobject jbitmap, jint usage)
 {
-    SkBitmap bitmap;
-    bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
-
+    android::graphics::Bitmap bitmap(_env, jbitmap);
     const void* ptr = bitmap.getPixels();
     jlong id = (jlong)(uintptr_t)rsAllocationCubeCreateFromBitmap((RsContext)con,
                                                       (RsType)type, (RsAllocationMipmapControl)mip,
-                                                      ptr, bitmap.computeByteSize(), usage);
+                                                      ptr, computeByteSize(bitmap), usage);
     return id;
 }
 
 static void
-nAllocationCopyFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jlong bitmapPtr)
+nAllocationCopyFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject jbitmap)
 {
-    SkBitmap bitmap;
-    bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
-    int w = bitmap.width();
-    int h = bitmap.height();
+    android::graphics::Bitmap bitmap(_env, jbitmap);
+    int w = bitmap.getInfo().width;
+    int h = bitmap.getInfo().height;
 
     const void* ptr = bitmap.getPixels();
     rsAllocation2DData((RsContext)con, (RsAllocation)alloc, 0, 0,
                        0, RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X,
-                       w, h, ptr, bitmap.computeByteSize(), 0);
+                       w, h, ptr, computeByteSize(bitmap), 0);
 }
 
 static void
-nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jlong bitmapPtr)
+nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject jbitmap)
 {
-    SkBitmap bitmap;
-    bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
-
+    android::graphics::Bitmap bitmap(_env, jbitmap);
     void* ptr = bitmap.getPixels();
-    rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, bitmap.computeByteSize());
+    rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, computeByteSize(bitmap));
     bitmap.notifyPixelsChanged();
 }
 
@@ -2867,12 +2863,12 @@
 {"rsnTypeGetNativeData",             "(JJ[J)V",                               (void*)nTypeGetNativeData },
 
 {"rsnAllocationCreateTyped",         "(JJIIJ)J",                              (void*)nAllocationCreateTyped },
-{"rsnAllocationCreateFromBitmap",    "(JJIJI)J",                              (void*)nAllocationCreateFromBitmap },
-{"rsnAllocationCreateBitmapBackedAllocation",    "(JJIJI)J",                  (void*)nAllocationCreateBitmapBackedAllocation },
-{"rsnAllocationCubeCreateFromBitmap","(JJIJI)J",                              (void*)nAllocationCubeCreateFromBitmap },
+{"rsnAllocationCreateFromBitmap",    "(JJILandroid/graphics/Bitmap;I)J",      (void*)nAllocationCreateFromBitmap },
+{"rsnAllocationCreateBitmapBackedAllocation",    "(JJILandroid/graphics/Bitmap;I)J", (void*)nAllocationCreateBitmapBackedAllocation },
+{"rsnAllocationCubeCreateFromBitmap","(JJILandroid/graphics/Bitmap;I)J",      (void*)nAllocationCubeCreateFromBitmap },
 
-{"rsnAllocationCopyFromBitmap",      "(JJJ)V",                                (void*)nAllocationCopyFromBitmap },
-{"rsnAllocationCopyToBitmap",        "(JJJ)V",                                (void*)nAllocationCopyToBitmap },
+{"rsnAllocationCopyFromBitmap",      "(JJLandroid/graphics/Bitmap;)V",        (void*)nAllocationCopyFromBitmap },
+{"rsnAllocationCopyToBitmap",        "(JJLandroid/graphics/Bitmap;)V",        (void*)nAllocationCopyToBitmap },
 
 {"rsnAllocationSyncAll",             "(JJI)V",                                (void*)nAllocationSyncAll },
 {"rsnAllocationSetupBufferQueue",    "(JJI)V",                                (void*)nAllocationSetupBufferQueue },
diff --git a/services/Android.bp b/services/Android.bp
index 1e11936..5afed6c 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -113,7 +113,7 @@
     srcs: [":services-sources"],
     installable: false,
     // TODO: remove the --hide options below
-    args: " --show-single-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.MODULE_LIBRARIES,process=android.annotation.SystemApi.Process.SYSTEM_SERVER\\)" +
         " --hide-annotation android.annotation.Hide" +
         " --hide-package com.google.android.startop.iorap" +
         " --hide ReferencesHidden" +
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6a6e2b2..58d3489 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -35,6 +35,7 @@
 import android.app.ActivityOptions;
 import android.app.AlertDialog;
 import android.app.PendingIntent;
+import android.app.RemoteAction;
 import android.appwidget.AppWidgetManagerInternal;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
@@ -148,6 +149,8 @@
     //       their capabilities are ready.
     private static final int WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS = 1000;
 
+    static final String FUNCTION_REGISTER_SYSTEM_ACTION = "registerSystemAction";
+    static final String FUNCTION_UNREGISTER_SYSTEM_ACTION = "unregisterSystemAction";
     private static final String FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE =
         "registerUiTestAutomationService";
 
@@ -253,6 +256,27 @@
         }
     }
 
+    @VisibleForTesting
+    AccessibilityManagerService(
+            Context context,
+            PackageManager packageManager,
+            AccessibilitySecurityPolicy securityPolicy,
+            SystemActionPerformer systemActionPerformer,
+            AccessibilityWindowManager a11yWindowManager,
+            AccessibilityDisplayListener a11yDisplayListener) {
+        mContext = context;
+        mPowerManager =  (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
+        mMainHandler = new MainHandler(mContext.getMainLooper());
+        mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
+        mPackageManager = packageManager;
+        mSecurityPolicy = securityPolicy;
+        mSystemActionPerformer = systemActionPerformer;
+        mA11yWindowManager = a11yWindowManager;
+        mA11yDisplayListener = a11yDisplayListener;
+        init();
+    }
+
     /**
      * Creates a new instance.
      *
@@ -260,21 +284,24 @@
      */
     public AccessibilityManagerService(Context context) {
         mContext = context;
-        mPackageManager = mContext.getPackageManager();
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mPowerManager =  (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
-        mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this);
         mMainHandler = new MainHandler(mContext.getMainLooper());
+        mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
+        mPackageManager = mContext.getPackageManager();
+        mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this);
         mSystemActionPerformer = new SystemActionPerformer(mContext, mWindowManagerService);
         mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
                 mWindowManagerService, this, mSecurityPolicy, this);
         mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
-        mSecurityPolicy.setAccessibilityWindowManager(mA11yWindowManager);
-        mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
+        init();
+    }
 
+    private void init() {
+        mSecurityPolicy.setAccessibilityWindowManager(mA11yWindowManager);
         registerBroadcastReceivers();
         new AccessibilityContentObserver(mMainHandler).register(
-                context.getContentResolver());
+                mContext.getContentResolver());
     }
 
     @Override
@@ -623,6 +650,30 @@
         event.recycle();
     }
 
+    /**
+     * This is the implementation of AccessibilityManager system API.
+     * System UI calls into this method through AccessibilityManager system API to register a
+     * system action.
+     */
+    @Override
+    public void registerSystemAction(RemoteAction action, int actionId) {
+        mSecurityPolicy.enforceCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY,
+                FUNCTION_REGISTER_SYSTEM_ACTION);
+        mSystemActionPerformer.registerSystemAction(actionId, action);
+    }
+
+    /**
+     * This is the implementation of AccessibilityManager system API.
+     * System UI calls into this method through AccessibilityManager system API to unregister a
+     * system action.
+     */
+    @Override
+    public void unregisterSystemAction(int actionId) {
+        mSecurityPolicy.enforceCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY,
+                FUNCTION_UNREGISTER_SYSTEM_ACTION);
+        mSystemActionPerformer.unregisterSystemAction(actionId);
+    }
+
     @Override
     public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
         synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index 1754926..11dcfef 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -157,8 +157,13 @@
     /**
      * This method is called to register a system action. If a system action is already registered
      * with the given id, the existing system action will be overwritten.
+     *
+     * This method is supposed to be package internal since this class is meant to be used by
+     * AccessibilityManagerService only. But Mockito has a bug which requiring this to be public
+     * to be mocked.
      */
-    void registerSystemAction(int id, RemoteAction action) {
+    @VisibleForTesting
+    public void registerSystemAction(int id, RemoteAction action) {
         synchronized (mSystemActionLock) {
             mRegisteredSystemActions.put(id, action);
         }
@@ -170,8 +175,13 @@
     /**
      * This method is called to unregister a system action previously registered through
      * registerSystemAction.
+     *
+     * This method is supposed to be package internal since this class is meant to be used by
+     * AccessibilityManagerService only. But Mockito has a bug which requiring this to be public
+     * to be mocked.
      */
-    void unregisterSystemAction(int id) {
+    @VisibleForTesting
+    public void unregisterSystemAction(int id) {
         synchronized (mSystemActionLock) {
             mRegisteredSystemActions.remove(id);
         }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 95cd8fc..1a6533d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -112,7 +112,6 @@
 import com.android.server.inputmethod.InputMethodManagerInternal;
 
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -311,20 +310,9 @@
     @NonNull
     private final InputMethodManagerInternal mInputMethodManagerInternal;
 
-    @GuardedBy("mLock")
-    @Nullable
-    private CompletableFuture<InlineSuggestionsRequest> mSuggestionsRequestFuture;
-
-    @GuardedBy("mLock")
-    @Nullable
-    private CompletableFuture<IInlineSuggestionsResponseCallback>
-            mInlineSuggestionsResponseCallbackFuture;
-
     @Nullable
     private InlineSuggestionsRequestCallbackImpl mInlineSuggestionsRequestCallback;
 
-    private static final int INLINE_REQUEST_TIMEOUT_MS = 1000;
-
     /**
      * Receiver of assist data from the app's {@link Activity}.
      */
@@ -422,7 +410,9 @@
                 final ArrayList<FillContext> contexts =
                         mergePreviousSessionLocked(/* forSave= */ false);
 
-                final InlineSuggestionsRequest suggestionsRequest = getInlineSuggestionsRequest();
+                final InlineSuggestionsRequest suggestionsRequest =
+                        mInlineSuggestionsRequestCallback != null
+                                ? mInlineSuggestionsRequestCallback.getRequest() : null;
 
                 request = new FillRequest(requestId, contexts, mClientState, flags,
                         suggestionsRequest);
@@ -620,13 +610,7 @@
     private void maybeRequestInlineSuggestionsRequestThenFillLocked(@NonNull ViewState viewState,
             int newState, int flags) {
         if (isInlineSuggestionsEnabled()) {
-            mSuggestionsRequestFuture = new CompletableFuture<>();
-            mInlineSuggestionsResponseCallbackFuture = new CompletableFuture<>();
-
-            if (mInlineSuggestionsRequestCallback == null) {
-                mInlineSuggestionsRequestCallback = new InlineSuggestionsRequestCallbackImpl(this);
-            }
-
+            mInlineSuggestionsRequestCallback = new InlineSuggestionsRequestCallbackImpl();
             mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
                     mComponentName, mCurrentViewId, mInlineSuggestionsRequestCallback);
         }
@@ -636,10 +620,14 @@
 
     private static final class InlineSuggestionsRequestCallbackImpl
             extends IInlineSuggestionsRequestCallback.Stub {
-        private final WeakReference<Session> mSession;
+        private static final int INLINE_REQUEST_TIMEOUT_MS = 1000;
 
-        private InlineSuggestionsRequestCallbackImpl(Session session) {
-            mSession = new WeakReference<>(session);
+        private final CompletableFuture<InlineSuggestionsRequest> mRequest;
+        private final CompletableFuture<IInlineSuggestionsResponseCallback> mResponseCallback;
+
+        private InlineSuggestionsRequestCallbackImpl() {
+            mRequest = new CompletableFuture<>();
+            mResponseCallback = new CompletableFuture<>();
         }
 
         @Override
@@ -648,13 +636,8 @@
                 Log.d(TAG, "inline suggestions request unsupported, "
                         + "falling back to regular autofill");
             }
-            final Session session = mSession.get();
-            if (session != null) {
-                synchronized (session.mLock) {
-                    session.mSuggestionsRequestFuture.cancel(true);
-                    session.mInlineSuggestionsResponseCallbackFuture.cancel(true);
-                }
-            }
+            mRequest.cancel(true);
+            mResponseCallback.cancel(true);
         }
 
         @Override
@@ -663,13 +646,36 @@
             if (sDebug) {
                 Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
             }
-            final Session session = mSession.get();
-            if (session != null) {
-                synchronized (session.mLock) {
-                    session.mSuggestionsRequestFuture.complete(request);
-                    session.mInlineSuggestionsResponseCallbackFuture.complete(callback);
-                }
+            mRequest.complete(request);
+            mResponseCallback.complete(callback);
+        }
+
+        @Nullable
+        private InlineSuggestionsRequest getRequest() {
+            try {
+                return mRequest.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            } catch (TimeoutException e) {
+                Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
+            } catch (CancellationException e) {
+                Log.w(TAG, "Inline suggestions request cancelled");
+            } catch (InterruptedException | ExecutionException e) {
+                throw new RuntimeException(e);
             }
+            return null;
+        }
+
+        @Nullable
+        private IInlineSuggestionsResponseCallback getResponseCallback() {
+            try {
+                return mResponseCallback.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            } catch (TimeoutException e) {
+                Log.w(TAG, "Exception getting inline suggestions callback in time: " + e);
+            } catch (CancellationException e) {
+                Log.w(TAG, "Inline suggestions callback cancelled");
+            } catch (InterruptedException | ExecutionException e) {
+                throw new RuntimeException(e);
+            }
+            return null;
         }
     }
 
@@ -2678,22 +2684,9 @@
      * Returns whether we made a request to show inline suggestions.
      */
     private boolean requestShowInlineSuggestions(FillResponse response) {
-        IInlineSuggestionsResponseCallback inlineContentCallback = null;
-        synchronized (mLock) {
-            if (mInlineSuggestionsResponseCallbackFuture != null) {
-                try {
-                    inlineContentCallback = mInlineSuggestionsResponseCallbackFuture.get(
-                            INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-                } catch (TimeoutException e) {
-                    Log.w(TAG, "Exception getting inline suggestions callback in time: " + e);
-                } catch (CancellationException e) {
-                    Log.w(TAG, "Inline suggestions callback cancelled");
-                } catch (InterruptedException | ExecutionException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        }
-
+        final IInlineSuggestionsResponseCallback inlineContentCallback =
+                mInlineSuggestionsRequestCallback != null
+                        ? mInlineSuggestionsRequestCallback.getResponseCallback() : null;
         if (inlineContentCallback == null) {
             Log.w(TAG, "Session input method callback is not set yet");
             return false;
@@ -3015,10 +3008,12 @@
 
         final AutofillId focusedId = AutofillId.withoutSession(mCurrentViewId);
 
-        final InlineSuggestionsRequest inlineSuggestionsRequest = getInlineSuggestionsRequest();
+        final InlineSuggestionsRequest inlineSuggestionsRequest =
+                mInlineSuggestionsRequestCallback != null
+                        ? mInlineSuggestionsRequestCallback.getRequest() : null;
         final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
-                getInlineSuggestionsResponseCallback();
-
+                mInlineSuggestionsRequestCallback != null
+                        ? mInlineSuggestionsRequestCallback.getResponseCallback() : null;
         remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
                 currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback);
 
@@ -3028,40 +3023,6 @@
         return mAugmentedAutofillDestroyer;
     }
 
-    @Nullable
-    private InlineSuggestionsRequest getInlineSuggestionsRequest() {
-        if (mSuggestionsRequestFuture != null) {
-            try {
-                return mSuggestionsRequestFuture.get(
-                        INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            } catch (TimeoutException e) {
-                Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
-            } catch (CancellationException e) {
-                Log.w(TAG, "Inline suggestions request cancelled");
-            } catch (InterruptedException | ExecutionException e) {
-                throw new RuntimeException(e);
-            }
-        }
-        return null;
-    }
-
-    @Nullable
-    private IInlineSuggestionsResponseCallback getInlineSuggestionsResponseCallback() {
-        if (mInlineSuggestionsResponseCallbackFuture != null) {
-            try {
-                return mInlineSuggestionsResponseCallbackFuture.get(
-                        INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            } catch (TimeoutException e) {
-                Log.w(TAG, "Exception getting inline suggestions callback in time: " + e);
-            } catch (CancellationException e) {
-                Log.w(TAG, "Inline suggestions callback cancelled");
-            } catch (InterruptedException | ExecutionException e) {
-                throw new RuntimeException(e);
-            }
-        }
-        return null;
-    }
-
     @GuardedBy("mLock")
     private void cancelAugmentedAutofillLocked() {
         final RemoteAugmentedAutofillService remoteService = mService
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bd8a361..c2e32d3 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -92,6 +92,7 @@
 import android.net.NetworkMisc;
 import android.net.NetworkMonitorManager;
 import android.net.NetworkPolicyManager;
+import android.net.NetworkProvider;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkScore;
@@ -219,6 +220,7 @@
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * @hide
@@ -595,6 +597,10 @@
     // sequence number of NetworkRequests
     private int mNextNetworkRequestId = 1;
 
+    // Sequence number for NetworkProvider IDs.
+    private final AtomicInteger mNextNetworkProviderId = new AtomicInteger(
+            NetworkProvider.FIRST_PROVIDER_ID);
+
     // NetworkRequest activity String log entries.
     private static final int MAX_NETWORK_REQUEST_LOGS = 20;
     private final LocalLog mNetworkRequestInfoLogs = new LocalLog(MAX_NETWORK_REQUEST_LOGS);
@@ -3029,25 +3035,9 @@
             if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
                 if (VDBG) log("NetworkFactory connected");
                 // Finish setting up the full connection
-                mNetworkFactoryInfos.get(msg.replyTo).asyncChannel.sendMessage(
-                        AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
-                // A network factory has connected.  Send it all current NetworkRequests.
-                for (NetworkRequestInfo nri : mNetworkRequests.values()) {
-                    if (nri.request.isListen()) continue;
-                    ensureRunningOnConnectivityServiceThread();
-                    NetworkAgentInfo nai = nri.mSatisfier;
-                    final int score;
-                    final int serial;
-                    if (nai != null) {
-                        score = nai.getCurrentScore();
-                        serial = nai.factorySerialNumber;
-                    } else {
-                        score = 0;
-                        serial = NetworkFactory.SerialNumber.NONE;
-                    }
-                    ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, serial,
-                            nri.request);
-                }
+                NetworkFactoryInfo nfi = mNetworkFactoryInfos.get(msg.replyTo);
+                nfi.completeConnection();
+                sendAllRequestsToFactory(nfi);
             } else {
                 loge("Error connecting NetworkFactory");
                 mNetworkFactoryInfos.remove(msg.obj);
@@ -3430,8 +3420,7 @@
             }
 
             for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
-                nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST,
-                        nri.request);
+                nfi.cancelRequest(nri.request);
             }
         } else {
             // listens don't have a singular affectedNetwork.  Check all networks to see
@@ -4927,15 +4916,70 @@
     private static class NetworkFactoryInfo {
         public final String name;
         public final Messenger messenger;
-        public final AsyncChannel asyncChannel;
+        private final AsyncChannel mAsyncChannel;
+        private final IBinder.DeathRecipient mDeathRecipient;
         public final int factorySerialNumber;
 
         NetworkFactoryInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
-                int factorySerialNumber) {
+                int factorySerialNumber, IBinder.DeathRecipient deathRecipient) {
             this.name = name;
             this.messenger = messenger;
-            this.asyncChannel = asyncChannel;
             this.factorySerialNumber = factorySerialNumber;
+            mAsyncChannel = asyncChannel;
+            mDeathRecipient = deathRecipient;
+
+            if ((mAsyncChannel == null) == (mDeathRecipient == null)) {
+                throw new AssertionError("Must pass exactly one of asyncChannel or deathRecipient");
+            }
+        }
+
+        boolean isLegacyNetworkFactory() {
+            return mAsyncChannel != null;
+        }
+
+        void sendMessageToNetworkProvider(int what, int arg1, int arg2, Object obj) {
+            try {
+                messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj));
+            } catch (RemoteException e) {
+                // Remote process died. Ignore; the death recipient will remove this
+                // NetworkFactoryInfo from mNetworkFactoryInfos.
+            }
+        }
+
+        void requestNetwork(NetworkRequest request, int score, int servingSerialNumber) {
+            if (isLegacyNetworkFactory()) {
+                mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
+                        servingSerialNumber, request);
+            } else {
+                sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
+                            servingSerialNumber, request);
+            }
+        }
+
+        void cancelRequest(NetworkRequest request) {
+            if (isLegacyNetworkFactory()) {
+                mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, request);
+            } else {
+                sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
+            }
+        }
+
+        void connect(Context context, Handler handler) {
+            if (isLegacyNetworkFactory()) {
+                mAsyncChannel.connect(context, handler, messenger);
+            } else {
+                try {
+                    messenger.getBinder().linkToDeath(mDeathRecipient, 0);
+                } catch (RemoteException e) {
+                    mDeathRecipient.binderDied();
+                }
+            }
+        }
+
+        void completeConnection() {
+            if (isLegacyNetworkFactory()) {
+                mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+            }
         }
     }
 
@@ -5306,6 +5350,11 @@
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
     }
 
+    /** Returns the next Network provider ID. */
+    public final int nextNetworkProviderId() {
+        return mNextNetworkProviderId.getAndIncrement();
+    }
+
     @Override
     public void releaseNetworkRequest(NetworkRequest networkRequest) {
         ensureNetworkRequestHasType(networkRequest);
@@ -5317,21 +5366,49 @@
     public int registerNetworkFactory(Messenger messenger, String name) {
         enforceNetworkFactoryPermission();
         NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel(),
-                NetworkFactory.SerialNumber.nextSerialNumber());
+                nextNetworkProviderId(), null /* deathRecipient */);
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));
         return nfi.factorySerialNumber;
     }
 
     private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) {
+        if (mNetworkFactoryInfos.containsKey(nfi.messenger)) {
+            // Avoid creating duplicates. even if an app makes a direct AIDL call.
+            // This will never happen if an app calls ConnectivityManager#registerNetworkProvider,
+            // as that will throw if a duplicate provider is registered.
+            Slog.e(TAG, "Attempt to register existing NetworkFactoryInfo "
+                    + mNetworkFactoryInfos.get(nfi.messenger).name);
+            return;
+        }
+
         if (DBG) log("Got NetworkFactory Messenger for " + nfi.name);
         mNetworkFactoryInfos.put(nfi.messenger, nfi);
-        nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger);
+        nfi.connect(mContext, mTrackerHandler);
+        if (!nfi.isLegacyNetworkFactory()) {
+            // Legacy NetworkFactories get their requests when their AsyncChannel connects.
+            sendAllRequestsToFactory(nfi);
+        }
+    }
+
+    @Override
+    public int registerNetworkProvider(Messenger messenger, String name) {
+        enforceNetworkFactoryPermission();
+        NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger,
+                null /* asyncChannel */, nextNetworkProviderId(),
+                () -> unregisterNetworkProvider(messenger));
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));
+        return nfi.factorySerialNumber;
+    }
+
+    @Override
+    public void unregisterNetworkProvider(Messenger messenger) {
+        enforceNetworkFactoryPermission();
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_FACTORY, messenger));
     }
 
     @Override
     public void unregisterNetworkFactory(Messenger messenger) {
-        enforceNetworkFactoryPermission();
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_FACTORY, messenger));
+        unregisterNetworkProvider(messenger);
     }
 
     private void handleUnregisterNetworkFactory(Messenger messenger) {
@@ -5343,6 +5420,12 @@
         if (DBG) log("unregisterNetworkFactory for " + nfi.name);
     }
 
+    @Override
+    public void declareNetworkRequestUnfulfillable(NetworkRequest request) {
+        enforceNetworkFactoryPermission();
+        mHandler.post(() -> handleReleaseNetworkRequest(request, Binder.getCallingUid(), true));
+    }
+
     // NOTE: Accessed on multiple threads, must be synchronized on itself.
     @GuardedBy("mNetworkForNetId")
     private final SparseArray<NetworkAgentInfo> mNetworkForNetId = new SparseArray<>();
@@ -5961,8 +6044,26 @@
             log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
         }
         for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
-            nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
-                    serial, networkRequest);
+            nfi.requestNetwork(networkRequest, score, serial);
+        }
+    }
+
+    /** Sends all current NetworkRequests to the specified factory. */
+    private void sendAllRequestsToFactory(NetworkFactoryInfo nfi) {
+        ensureRunningOnConnectivityServiceThread();
+        for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+            if (nri.request.isListen()) continue;
+            NetworkAgentInfo nai = nri.mSatisfier;
+            final int score;
+            final int serial;
+            if (nai != null) {
+                score = nai.getCurrentScore();
+                serial = nai.factorySerialNumber;
+            } else {
+                score = 0;
+                serial = NetworkFactory.SerialNumber.NONE;
+            }
+            nfi.requestNetwork(nri.request, score, serial);
         }
     }
 
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index c5f1923..0fc5340 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -3192,6 +3192,8 @@
             }
             ipw.decreaseIndent();
 
+            mRequestStatistics.history.dump(ipw);
+
             ipw.println("Last Known Locations:");
             ipw.increaseIndent();
             for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 1f73650..05d8360 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -58,10 +58,12 @@
 import android.net.NetworkStats;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
-import android.net.TetherConfigParcel;
 import android.net.TetherStatsParcel;
 import android.net.UidRange;
 import android.net.UidRangeParcel;
+import android.net.shared.NetdUtils;
+import android.net.shared.RouteUtils;
+import android.net.shared.RouteUtils.ModifyOperation;
 import android.net.util.NetdService;
 import android.os.BatteryStats;
 import android.os.Binder;
@@ -898,48 +900,14 @@
 
     @Override
     public void addRoute(int netId, RouteInfo route) {
-        modifyRoute(MODIFY_OPERATION_ADD, netId, route);
+        NetworkStack.checkNetworkStackPermission(mContext);
+        RouteUtils.modifyRoute(mNetdService, ModifyOperation.ADD, netId, route);
     }
 
     @Override
     public void removeRoute(int netId, RouteInfo route) {
-        modifyRoute(MODIFY_OPERATION_REMOVE, netId, route);
-    }
-
-    private void modifyRoute(boolean add, int netId, RouteInfo route) {
         NetworkStack.checkNetworkStackPermission(mContext);
-
-        final String ifName = route.getInterface();
-        final String dst = route.getDestination().toString();
-        final String nextHop;
-
-        switch (route.getType()) {
-            case RouteInfo.RTN_UNICAST:
-                if (route.hasGateway()) {
-                    nextHop = route.getGateway().getHostAddress();
-                } else {
-                    nextHop = INetd.NEXTHOP_NONE;
-                }
-                break;
-            case RouteInfo.RTN_UNREACHABLE:
-                nextHop = INetd.NEXTHOP_UNREACHABLE;
-                break;
-            case RouteInfo.RTN_THROW:
-                nextHop = INetd.NEXTHOP_THROW;
-                break;
-            default:
-                nextHop = INetd.NEXTHOP_NONE;
-                break;
-        }
-        try {
-            if (add) {
-                mNetdService.networkAddRoute(netId, ifName, dst, nextHop);
-            } else {
-                mNetdService.networkRemoveRoute(netId, ifName, dst, nextHop);
-            }
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
+        RouteUtils.modifyRoute(mNetdService, ModifyOperation.REMOVE, netId, route);
     }
 
     private ArrayList<String> readRouteList(String filename) {
@@ -1023,12 +991,8 @@
     @Override
     public void startTetheringWithConfiguration(boolean usingLegacyDnsProxy, String[] dhcpRange) {
         NetworkStack.checkNetworkStackPermission(mContext);
-        // an odd number of addrs will fail
         try {
-            final TetherConfigParcel config = new TetherConfigParcel();
-            config.usingLegacyDnsProxy = usingLegacyDnsProxy;
-            config.dhcpRanges = dhcpRange;
-            mNetdService.tetherStartWithConfiguration(config);
+            NetdUtils.tetherStart(mNetdService, usingLegacyDnsProxy, dhcpRange);
         } catch (RemoteException | ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
@@ -1060,26 +1024,21 @@
     public void tetherInterface(String iface) {
         NetworkStack.checkNetworkStackPermission(mContext);
         try {
-            mNetdService.tetherInterfaceAdd(iface);
+            final LinkAddress addr = getInterfaceConfig(iface).getLinkAddress();
+            final IpPrefix dest = new IpPrefix(addr.getAddress(), addr.getPrefixLength());
+            NetdUtils.tetherInterface(mNetdService, iface, dest);
         } catch (RemoteException | ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
-        List<RouteInfo> routes = new ArrayList<>();
-        // The RouteInfo constructor truncates the LinkAddress to a network prefix, thus making it
-        // suitable to use as a route destination.
-        routes.add(new RouteInfo(getInterfaceConfig(iface).getLinkAddress(), null, iface));
-        addInterfaceToLocalNetwork(iface, routes);
     }
 
     @Override
     public void untetherInterface(String iface) {
         NetworkStack.checkNetworkStackPermission(mContext);
         try {
-            mNetdService.tetherInterfaceRemove(iface);
+            NetdUtils.untetherInterface(mNetdService, iface);
         } catch (RemoteException | ServiceSpecificException e) {
             throw new IllegalStateException(e);
-        } finally {
-            removeInterfaceFromLocalNetwork(iface);
         }
     }
 
@@ -2122,16 +2081,8 @@
     @Override
     public void addInterfaceToLocalNetwork(String iface, List<RouteInfo> routes) {
         modifyInterfaceInNetwork(MODIFY_OPERATION_ADD, INetd.LOCAL_NET_ID, iface);
-
-        for (RouteInfo route : routes) {
-            if (!route.isDefaultRoute()) {
-                modifyRoute(MODIFY_OPERATION_ADD, INetd.LOCAL_NET_ID, route);
-            }
-        }
-
-        // IPv6 link local should be activated always.
-        modifyRoute(MODIFY_OPERATION_ADD, INetd.LOCAL_NET_ID,
-                new RouteInfo(new IpPrefix("fe80::/64"), null, iface));
+        // modifyInterfaceInNetwork already check calling permission.
+        RouteUtils.addRoutesToLocalNetwork(mNetdService, iface, routes);
     }
 
     @Override
@@ -2141,17 +2092,8 @@
 
     @Override
     public int removeRoutesFromLocalNetwork(List<RouteInfo> routes) {
-        int failures = 0;
-
-        for (RouteInfo route : routes) {
-            try {
-                modifyRoute(MODIFY_OPERATION_REMOVE, INetd.LOCAL_NET_ID, route);
-            } catch (IllegalStateException e) {
-                failures++;
-            }
-        }
-
-        return failures;
+        NetworkStack.checkNetworkStackPermission(mContext);
+        return RouteUtils.removeRoutesFromLocalNetwork(mNetdService, routes);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
index cfe56052..d20936c 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
@@ -36,11 +36,11 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.SystemClock;
+import android.os.TimestampedValue;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.NtpTrustedTime;
 import android.util.TimeUtils;
-import android.util.TimestampedValue;
 
 import com.android.internal.util.DumpUtils;
 
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index deff440..7b4fd37 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -199,13 +199,14 @@
         mSystemClock = clock;
         mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS;
         loadFromFile();
+        sPackageWatchdog = this;
     }
 
     /** Creates or gets singleton instance of PackageWatchdog. */
     public static PackageWatchdog getInstance(Context context) {
         synchronized (PackageWatchdog.class) {
             if (sPackageWatchdog == null) {
-                sPackageWatchdog = new PackageWatchdog(context);
+                new PackageWatchdog(context);
             }
             return sPackageWatchdog;
         }
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index fb1a962..3dafc64 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -18,8 +18,12 @@
 
 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
 
+import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
 import android.os.Build;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -36,8 +40,12 @@
 import android.util.SparseArray;
 import android.util.StatsLog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.PackageWatchdog.FailureReasons;
+import com.android.server.PackageWatchdog.PackageHealthObserver;
+import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
 import com.android.server.am.SettingsToPropertiesMapper;
 import com.android.server.utils.FlagNamespaceUtils;
 
@@ -79,19 +87,30 @@
     @VisibleForTesting
     static final long BOOT_TRIGGER_WINDOW_MILLIS = 600 * DateUtils.SECOND_IN_MILLIS;
     @VisibleForTesting
-    static final long PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS = 30 * DateUtils.SECOND_IN_MILLIS;
-    @VisibleForTesting
     static final String TAG = "RescueParty";
 
+    private static final String NAME = "rescue-party-observer";
+
+
     private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
     private static final String PROP_RESCUE_BOOT_START = "sys.rescue_boot_start";
     private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device";
 
+    private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
+            | ApplicationInfo.FLAG_SYSTEM;
+
+
     /** Threshold for boot loops */
     private static final Threshold sBoot = new BootThreshold();
     /** Threshold for app crash loops */
     private static SparseArray<Threshold> sApps = new SparseArray<>();
 
+    /** Register the Rescue Party observer as a Package Watchdog health observer */
+    public static void registerHealthObserver(Context context) {
+        PackageWatchdog.getInstance(context).registerHealthObserver(
+                RescuePartyObserver.getInstance(context));
+    }
+
     private static boolean isDisabled() {
         // Check if we're explicitly enabled for testing
         if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) {
@@ -135,24 +154,6 @@
     }
 
     /**
-     * Take note of a persistent app or apex module crash. If we notice too many of these
-     * events happening in rapid succession, we'll send out a rescue party.
-     */
-    public static void noteAppCrash(Context context, int uid) {
-        if (isDisabled()) return;
-        Threshold t = sApps.get(uid);
-        if (t == null) {
-            t = new AppThreshold(uid);
-            sApps.put(uid, t);
-        }
-        if (t.incrementAndTest()) {
-            t.reset();
-            incrementRescueLevel(t.uid);
-            executeRescueLevel(context);
-        }
-    }
-
-    /**
      * Check if we're currently attempting to reboot for a factory reset.
      */
     public static boolean isAttemptingFactoryReset() {
@@ -171,11 +172,6 @@
     @VisibleForTesting
     static void resetAllThresholds() {
         sBoot.reset();
-
-        for (int i = 0; i < sApps.size(); i++) {
-            Threshold appThreshold = sApps.get(sApps.keyAt(i));
-            appThreshold.reset();
-        }
     }
 
     @VisibleForTesting
@@ -243,6 +239,28 @@
                 FlagNamespaceUtils.NAMESPACE_NO_PACKAGE);
     }
 
+    private static int mapRescueLevelToUserImpact(int rescueLevel) {
+        switch(rescueLevel) {
+            case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
+            case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
+                return PackageHealthObserverImpact.USER_IMPACT_LOW;
+            case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
+            case LEVEL_FACTORY_RESET:
+                return PackageHealthObserverImpact.USER_IMPACT_HIGH;
+            default:
+                return PackageHealthObserverImpact.USER_IMPACT_NONE;
+        }
+    }
+
+    private static int getPackageUid(Context context, String packageName) {
+        try {
+            return context.getPackageManager().getPackageUid(packageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            // Since UIDs are always >= 0, this value means the UID could not be determined.
+            return -1;
+        }
+    }
+
     private static void resetAllSettings(Context context, int mode) throws Exception {
         // Try our best to reset all settings possible, and once finished
         // rethrow any exception that we encountered
@@ -271,6 +289,89 @@
     }
 
     /**
+     * Handle mitigation action for package failures. This observer will be register to Package
+     * Watchdog and will receive calls about package failures. This observer is persistent so it
+     * may choose to mitigate failures for packages it has not explicitly asked to observe.
+     */
+    public static class RescuePartyObserver implements PackageHealthObserver {
+
+        private Context mContext;
+
+        @GuardedBy("RescuePartyObserver.class")
+        static RescuePartyObserver sRescuePartyObserver;
+
+        private RescuePartyObserver(Context context) {
+            mContext = context;
+        }
+
+        /** Creates or gets singleton instance of RescueParty. */
+        public static RescuePartyObserver getInstance(Context context) {
+            synchronized (RescuePartyObserver.class) {
+                if (sRescuePartyObserver == null) {
+                    sRescuePartyObserver = new RescuePartyObserver(context);
+                }
+                return sRescuePartyObserver;
+            }
+        }
+
+        @Override
+        public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
+                @FailureReasons int failureReason) {
+            if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
+                    || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
+                int rescueLevel = MathUtils.constrain(
+                        SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) + 1,
+                        LEVEL_NONE, LEVEL_FACTORY_RESET);
+                return mapRescueLevelToUserImpact(rescueLevel);
+            } else {
+                return PackageHealthObserverImpact.USER_IMPACT_NONE;
+            }
+        }
+
+        @Override
+        public boolean execute(@Nullable VersionedPackage failedPackage,
+                @FailureReasons int failureReason) {
+            if (isDisabled()) {
+                return false;
+            }
+            if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
+                    || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
+                int triggerUid = getPackageUid(mContext, failedPackage.getPackageName());
+                incrementRescueLevel(triggerUid);
+                executeRescueLevel(mContext);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public boolean isPersistent() {
+            return true;
+        }
+
+        @Override
+        public boolean mayObservePackage(String packageName) {
+            PackageManager pm = mContext.getPackageManager();
+            try {
+                // A package is a Mainline module if this is non-null
+                if (pm.getModuleInfo(packageName, 0) != null) {
+                    return true;
+                }
+                ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+                return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
+            } catch (PackageManager.NameNotFoundException e) {
+                return false;
+            }
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+    }
+
+    /**
      * Threshold that can be triggered if a number of events occur within a
      * window of time.
      */
@@ -349,27 +450,6 @@
         }
     }
 
-    /**
-     * Specialization of {@link Threshold} for monitoring app crashes. It stores
-     * counters in memory.
-     */
-    private static class AppThreshold extends Threshold {
-        private int count;
-        private long start;
-
-        public AppThreshold(int uid) {
-            // We're interested in TRIGGER_COUNT events in any
-            // PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS second period; apps crash pretty quickly
-            // so we can keep a tight leash on them.
-            super(uid, TRIGGER_COUNT, PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS);
-        }
-
-        @Override public int getCount() { return count; }
-        @Override public void setCount(int count) { this.count = count; }
-        @Override public long getStart() { return start; }
-        @Override public void setStart(long start) { this.start = start; }
-    }
-
     private static int[] getAllUserIds() {
         int[] userIds = { UserHandle.USER_SYSTEM };
         try {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 3d455ee..db54214 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.ACCESS_MTP;
 import static android.Manifest.permission.INSTALL_PACKAGES;
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
@@ -111,6 +112,7 @@
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
 import android.provider.DeviceConfig;
+import android.provider.Downloads;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.sysprop.VoldProperties;
@@ -367,6 +369,8 @@
 
     private volatile int mMediaStoreAuthorityAppId = -1;
 
+    private volatile int mDownloadsAuthorityAppId = -1;
+
     private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
 
     private final Installer mInstaller;
@@ -1788,6 +1792,15 @@
             mMediaStoreAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
         }
 
+        provider = mPmInternal.resolveContentProvider(
+                Downloads.Impl.AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE
+                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                UserHandle.getUserId(UserHandle.USER_SYSTEM));
+
+        if (provider != null) {
+            mDownloadsAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
+        }
+
         try {
             mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, mAppOpsCallback);
             mIAppOpsService.startWatchingMode(OP_LEGACY_STORAGE, null, mAppOpsCallback);
@@ -3881,6 +3894,19 @@
                 return Zygote.MOUNT_EXTERNAL_PASS_THROUGH;
             }
 
+            if (mIsFuseEnabled && mDownloadsAuthorityAppId == UserHandle.getAppId(uid)) {
+                // DownloadManager can write in app-private directories on behalf of apps;
+                // give it write access to Android/
+                return Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE;
+            }
+
+            final boolean hasMtp = mIPackageManager.checkUidPermission(ACCESS_MTP, uid) ==
+                    PERMISSION_GRANTED;
+            if (mIsFuseEnabled && hasMtp) {
+                // The process hosting the MTP server should be able to write in Android/
+                return Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE;
+            }
+
             // Determine if caller is holding runtime permission
             final boolean hasRead = StorageManager.checkPermissionAndCheckOp(mContext, false, 0,
                     uid, packageName, READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE);
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 2091c2a..9ffe89c 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -1059,8 +1059,8 @@
             if (Sandman.shouldStartDockApp(getContext(), homeIntent)) {
                 try {
                     int result = ActivityTaskManager.getService().startActivityWithConfig(
-                            null, null, homeIntent, null, null, null, 0, 0,
-                            mConfiguration, null, UserHandle.USER_CURRENT);
+                            null, getContext().getBasePackageName(), homeIntent, null, null, null,
+                            0, 0, mConfiguration, null, UserHandle.USER_CURRENT);
                     if (ActivityManager.isStartResultSuccessful(result)) {
                         dockAppStarted = true;
                     } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 454941c..a60b09f 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -39,8 +39,10 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.StatsLog;
 
+import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.ZygoteConnectionConstants;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.wm.SurfaceAnimationThread;
@@ -606,13 +608,18 @@
             pids.add(Process.myPid());
             if (mPhonePid > 0) pids.add(mPhonePid);
 
+            long anrTime = SystemClock.uptimeMillis();
+            ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(false);
             final File stack = ActivityManagerService.dumpStackTraces(
-                    pids, null, null, getInterestingNativePids());
+                    pids, processCpuTracker, new SparseArray<>(), getInterestingNativePids());
 
             // Give some extra time to make sure the stack traces get written.
             // The system's been hanging for a minute, another second or two won't hurt much.
             SystemClock.sleep(5000);
 
+            processCpuTracker.update();
+            String cpuInfo = processCpuTracker.printCurrentState(anrTime);
+
             // Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel log
             doSysRq('w');
             doSysRq('l');
@@ -627,7 +634,7 @@
                         if (mActivity != null) {
                             mActivity.addErrorToDropBox(
                                     "watchdog", null, "system_server", null, null, null,
-                                    subject, null, stack, null);
+                                    subject, cpuInfo, stack, null);
                         }
                         StatsLog.write(StatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject);
                     }
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 4b48ef9..143474b 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -413,6 +413,11 @@
                 case MESSAGE_ADB_CLEAR: {
                     Slog.d(TAG, "Received a request to clear the adb authorizations");
                     mConnectedKeys.clear();
+                    // If the key store has not yet been instantiated then do so now; this avoids
+                    // the unnecessary creation of the key store when adb is not enabled.
+                    if (mAdbKeyStore == null) {
+                        mAdbKeyStore = new AdbKeyStore();
+                    }
                     mAdbKeyStore.deleteKeyStore();
                     cancelJobToUpdateAdbKeyStore();
                     break;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index a4c44fa..d7ad1c2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2934,33 +2934,37 @@
         }
         ArraySet<Long> enabled = new ArraySet<>();
         ArraySet<Long> disabled = new ArraySet<>();
-        switch (toggleValue) {
-            case "enable":
-                enabled.add(changeId);
-                pw.println("Enabled change " + changeId + " for " + packageName + ".");
-                CompatibilityChangeConfig overrides =
-                        new CompatibilityChangeConfig(
-                                new Compatibility.ChangeConfig(enabled, disabled));
-                platformCompat.setOverrides(overrides, packageName);
-                return 0;
-            case "disable":
-                disabled.add(changeId);
-                pw.println("Disabled change " + changeId + " for " + packageName + ".");
-                overrides =
-                        new CompatibilityChangeConfig(
-                                new Compatibility.ChangeConfig(enabled, disabled));
-                platformCompat.setOverrides(overrides, packageName);
-                return 0;
-            case "reset":
-                if (platformCompat.clearOverride(changeId, packageName)) {
-                    pw.println("Reset change " + changeId + " for " + packageName
-                            + " to default value.");
-                } else {
-                    pw.println("No override exists for changeId " + changeId + ".");
-                }
-                return 0;
-            default:
-                pw.println("Invalid toggle value: '" + toggleValue + "'.");
+        try {
+            switch (toggleValue) {
+                case "enable":
+                    enabled.add(changeId);
+                    CompatibilityChangeConfig overrides =
+                            new CompatibilityChangeConfig(
+                                    new Compatibility.ChangeConfig(enabled, disabled));
+                    platformCompat.setOverrides(overrides, packageName);
+                    pw.println("Enabled change " + changeId + " for " + packageName + ".");
+                    return 0;
+                case "disable":
+                    disabled.add(changeId);
+                    overrides =
+                            new CompatibilityChangeConfig(
+                                    new Compatibility.ChangeConfig(enabled, disabled));
+                    platformCompat.setOverrides(overrides, packageName);
+                    pw.println("Disabled change " + changeId + " for " + packageName + ".");
+                    return 0;
+                case "reset":
+                    if (platformCompat.clearOverride(changeId, packageName)) {
+                        pw.println("Reset change " + changeId + " for " + packageName
+                                + " to default value.");
+                    } else {
+                        pw.println("No override exists for changeId " + changeId + ".");
+                    }
+                    return 0;
+                default:
+                    pw.println("Invalid toggle value: '" + toggleValue + "'.");
+            }
+        } catch (SecurityException e) {
+            pw.println(e.getMessage());
         }
         return -1;
     }
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 5e48dcf..8071f52 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -33,8 +33,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.ModuleInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.net.Uri;
 import android.os.Binder;
@@ -56,7 +54,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.server.PackageWatchdog;
-import com.android.server.RescueParty;
 import com.android.server.wm.WindowProcessController;
 
 import java.io.FileDescriptor;
@@ -423,28 +420,6 @@
         }
 
         if (r != null) {
-            boolean isApexModule = false;
-            try {
-                for (String androidPackage : r.getPackageList()) {
-                    ModuleInfo moduleInfo = mContext.getPackageManager().getModuleInfo(
-                            androidPackage, /*flags=*/ 0);
-                    if (moduleInfo != null) {
-                        isApexModule = true;
-                        break;
-                    }
-                }
-            } catch (IllegalStateException | PackageManager.NameNotFoundException e) {
-                // Call to PackageManager#getModuleInfo() can result in NameNotFoundException or
-                // IllegalStateException. In case they are thrown, there isn't much we can do
-                // other than proceed with app crash handling.
-            }
-
-            if (r.isPersistent() || isApexModule) {
-                // If a persistent app or apex module is stuck in a crash loop, the device isn't
-                // very usable, so we want to consider sending out a rescue party.
-                RescueParty.noteAppCrash(mContext, r.uid);
-            }
-
             mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode(),
                     PackageWatchdog.FAILURE_REASON_APP_CRASH);
         }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index a1e1f29..b9f2e76 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -103,6 +103,7 @@
 import com.android.internal.util.MemInfoReader;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
+import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.dex.DexManager;
@@ -357,6 +358,8 @@
 
     private boolean mAppDataIsolationEnabled = false;
 
+    private ArrayList<String> mAppDataIsolationWhitelistedApps;
+
     /**
      * Temporary to avoid allocations.  Protected by main lock.
      */
@@ -645,6 +648,9 @@
         // want some apps enabled while some apps disabled
         mAppDataIsolationEnabled =
                 SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
+        mAppDataIsolationWhitelistedApps = new ArrayList<>(
+                SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
+
 
         if (sKillHandler == null) {
             sKillThread = new ServiceThread(TAG + ":kill",
@@ -1555,20 +1561,27 @@
                 } catch (RemoteException e) {
                     throw e.rethrowAsRuntimeException();
                 }
-
+                int numGids = 3;
+                if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER
+                        || mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
+                    numGids++;
+                }
                 /*
                  * Add shared application and profile GIDs so applications can share some
                  * resources like shared libraries and access user-wide resources
                  */
                 if (ArrayUtils.isEmpty(permGids)) {
-                    gids = new int[3];
+                    gids = new int[numGids];
                 } else {
-                    gids = new int[permGids.length + 3];
-                    System.arraycopy(permGids, 0, gids, 3, permGids.length);
+                    gids = new int[permGids.length + numGids];
+                    System.arraycopy(permGids, 0, gids, numGids, permGids.length);
                 }
                 gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
                 gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
                 gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
+                if (numGids > 3) {
+                    gids[3] = Process.SDCARD_RW_GID;
+                }
 
                 // Replace any invalid GIDs
                 if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
@@ -1905,6 +1918,16 @@
                 result.put(packageName, Pair.create(volumeUuid, inode));
             }
         }
+        if (mAppDataIsolationWhitelistedApps != null) {
+            for (String packageName : mAppDataIsolationWhitelistedApps) {
+                String volumeUuid = pmInt.getPackage(packageName).getVolumeUuid();
+                long inode = pmInt.getCeDataInode(packageName, userId);
+                if (inode != 0) {
+                    result.put(packageName, Pair.create(volumeUuid, inode));
+                }
+            }
+        }
+
         return result;
     }
 
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index cf83dd6..f15d999 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -17,8 +17,10 @@
 package com.android.server.compat;
 
 import android.compat.Compatibility.ChangeConfig;
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.os.Environment;
+import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.LongArray;
 import android.util.LongSparseArray;
@@ -26,8 +28,11 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.AndroidBuildClassifier;
 import com.android.internal.compat.CompatibilityChangeConfig;
 import com.android.internal.compat.CompatibilityChangeInfo;
+import com.android.internal.compat.IOverrideValidator;
+import com.android.internal.compat.OverrideAllowedState;
 import com.android.server.compat.config.Change;
 import com.android.server.compat.config.XmlParser;
 
@@ -54,22 +59,14 @@
 
     private static final String TAG = "CompatConfig";
 
-    private static final CompatConfig sInstance = new CompatConfig().initConfigFromLib(
-            Environment.buildPath(
-                    Environment.getRootDirectory(), "etc", "compatconfig"));
-
     @GuardedBy("mChanges")
     private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
 
-    @VisibleForTesting
-    CompatConfig() {
-    }
+    private IOverrideValidator mOverrideValidator;
 
-    /**
-     * @return The static instance of this class to be used within the system server.
-     */
-    static CompatConfig get() {
-        return sInstance;
+    @VisibleForTesting
+    CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
+        mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this);
     }
 
     /**
@@ -159,8 +156,12 @@
      * @param enabled     If the change should be enabled or disabled.
      * @return {@code true} if the change existed before adding the override.
      */
-    boolean addOverride(long changeId, String packageName, boolean enabled) {
+    boolean addOverride(long changeId, String packageName, boolean enabled)
+            throws RemoteException, SecurityException {
         boolean alreadyKnown = true;
+        OverrideAllowedState allowedState =
+                mOverrideValidator.getOverrideAllowedState(changeId, packageName);
+        allowedState.enforce(changeId, packageName);
         synchronized (mChanges) {
             CompatChange c = mChanges.get(changeId);
             if (c == null) {
@@ -186,6 +187,20 @@
     }
 
     /**
+     * Returns the minimum sdk version for which this change should be enabled (or 0 if it is not
+     * target sdk gated).
+     */
+    int minTargetSdkForChangeId(long changeId) {
+        synchronized (mChanges) {
+            CompatChange c = mChanges.get(changeId);
+            if (c == null) {
+                return 0;
+            }
+            return c.getEnableAfterTargetSdk();
+        }
+    }
+
+    /**
      * Removes an override previously added via {@link #addOverride(long, String, boolean)}. This
      * restores the default behaviour for the given change and app, once any app processes have been
      * restarted.
@@ -194,34 +209,44 @@
      * @param packageName The app package name that was overridden.
      * @return {@code true} if an override existed;
      */
-    boolean removeOverride(long changeId, String packageName) {
+    boolean removeOverride(long changeId, String packageName)
+            throws RemoteException, SecurityException {
         boolean overrideExists = false;
         synchronized (mChanges) {
             CompatChange c = mChanges.get(changeId);
-            if (c != null) {
-                overrideExists = true;
-                c.removePackageOverride(packageName);
+            try {
+                if (c != null) {
+                    OverrideAllowedState allowedState =
+                            mOverrideValidator.getOverrideAllowedState(changeId, packageName);
+                    allowedState.enforce(changeId, packageName);
+                    overrideExists = true;
+                    c.removePackageOverride(packageName);
+                }
+            } catch (RemoteException e) {
+                // Should never occur, since validator is in the same process.
+                throw new RuntimeException("Unable to call override validator!", e);
             }
         }
         return overrideExists;
     }
 
     /**
-     * Overrides the enabled state for a given change and app. This method is intended to be used
-     * *only* for debugging purposes.
+     * Overrides the enabled state for a given change and app.
      *
      * <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
      *
      * @param overrides   list of overrides to default changes config.
      * @param packageName app for which the overrides will be applied.
      */
-    void addOverrides(CompatibilityChangeConfig overrides, String packageName) {
+    void addOverrides(CompatibilityChangeConfig overrides, String packageName)
+            throws RemoteException, SecurityException {
         synchronized (mChanges) {
             for (Long changeId : overrides.enabledChanges()) {
                 addOverride(changeId, packageName, true);
             }
             for (Long changeId : overrides.disabledChanges()) {
                 addOverride(changeId, packageName, false);
+
             }
         }
     }
@@ -235,10 +260,22 @@
      *
      * @param packageName The package for which the overrides should be purged.
      */
-    void removePackageOverrides(String packageName) {
+    void removePackageOverrides(String packageName) throws RemoteException, SecurityException {
         synchronized (mChanges) {
             for (int i = 0; i < mChanges.size(); ++i) {
-                mChanges.valueAt(i).removePackageOverride(packageName);
+                try {
+                    CompatChange change = mChanges.valueAt(i);
+                    OverrideAllowedState allowedState =
+                            mOverrideValidator.getOverrideAllowedState(change.getId(),
+                                                                       packageName);
+                    allowedState.enforce(change.getId(), packageName);
+                    if (change != null) {
+                        mChanges.valueAt(i).removePackageOverride(packageName);
+                    }
+                } catch (RemoteException e) {
+                    // Should never occur, since validator is in the same process.
+                    throw new RuntimeException("Unable to call override validator!", e);
+                }
             }
         }
     }
@@ -326,17 +363,23 @@
         }
     }
 
-    CompatConfig initConfigFromLib(File libraryDir) {
+    static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) {
+        CompatConfig config = new CompatConfig(androidBuildClassifier, context);
+        config.initConfigFromLib(Environment.buildPath(
+                Environment.getRootDirectory(), "etc", "compatconfig"));
+        return config;
+    }
+
+    void initConfigFromLib(File libraryDir) {
         if (!libraryDir.exists() || !libraryDir.isDirectory()) {
             Slog.e(TAG, "No directory " + libraryDir + ", skipping");
-            return this;
+            return;
         }
         for (File f : libraryDir.listFiles()) {
             Slog.d(TAG, "Found a config file: " + f.getPath());
             //TODO(b/138222363): Handle duplicate ids across config files.
             readConfig(f);
         }
-        return this;
     }
 
     private void readConfig(File configFile) {
@@ -350,4 +393,7 @@
         }
     }
 
+    IOverrideValidator getOverrideValidator() {
+        return mOverrideValidator;
+    }
 }
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
new file mode 100644
index 0000000..dfc0080
--- /dev/null
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -0,0 +1,94 @@
+/*
+ * 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.server.compat;
+
+import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
+import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
+import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
+import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
+import static com.android.internal.compat.OverrideAllowedState.PACKAGE_DOES_NOT_EXIST;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.AndroidBuildClassifier;
+import com.android.internal.compat.IOverrideValidator;
+import com.android.internal.compat.OverrideAllowedState;
+
+/**
+ * Implementation of the policy for allowing compat change overrides.
+ */
+public class OverrideValidatorImpl extends IOverrideValidator.Stub {
+
+    private AndroidBuildClassifier mAndroidBuildClassifier;
+    private Context mContext;
+    private CompatConfig mCompatConfig;
+
+    @VisibleForTesting
+    OverrideValidatorImpl(AndroidBuildClassifier androidBuildClassifier,
+                          Context context, CompatConfig config) {
+        mAndroidBuildClassifier = androidBuildClassifier;
+        mContext = context;
+        mCompatConfig = config;
+    }
+
+    @Override
+    public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) {
+        boolean debuggableBuild = false;
+        boolean finalBuild = false;
+
+        debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild();
+        finalBuild = mAndroidBuildClassifier.isFinalBuild();
+
+        // Allow any override for userdebug or eng builds.
+        if (debuggableBuild) {
+            return new OverrideAllowedState(ALLOWED, -1, -1);
+        }
+        PackageManager packageManager = mContext.getPackageManager();
+        if (packageManager == null) {
+            throw new IllegalStateException("No PackageManager!");
+        }
+        ApplicationInfo applicationInfo;
+        try {
+            applicationInfo = packageManager.getApplicationInfo(packageName, 0);
+        } catch (NameNotFoundException e) {
+            return new OverrideAllowedState(PACKAGE_DOES_NOT_EXIST, -1, -1);
+        }
+        int appTargetSdk = applicationInfo.targetSdkVersion;
+        // Only allow overriding debuggable apps.
+        if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+            return new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1);
+        }
+        int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId);
+        // Do not allow overriding non-target sdk gated changes on user builds
+        if (minTargetSdk == -1) {
+            return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, minTargetSdk);
+        }
+        // Allow overriding any change for debuggable apps on non-final builds.
+        if (!finalBuild) {
+            return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk);
+        }
+        // Only allow to opt-in for a targetSdk gated change.
+        if (applicationInfo.targetSdkVersion < minTargetSdk) {
+            return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk);
+        }
+        return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, minTargetSdk);
+    }
+}
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 6ec4b9f..bb8b12e 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -27,9 +27,12 @@
 import android.util.Slog;
 import android.util.StatsLog;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.AndroidBuildClassifier;
 import com.android.internal.compat.ChangeReporter;
 import com.android.internal.compat.CompatibilityChangeConfig;
 import com.android.internal.compat.CompatibilityChangeInfo;
+import com.android.internal.compat.IOverrideValidator;
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.util.DumpUtils;
 import com.android.server.LocalServices;
@@ -46,11 +49,21 @@
 
     private final Context mContext;
     private final ChangeReporter mChangeReporter;
+    private final CompatConfig mCompatConfig;
 
     public PlatformCompat(Context context) {
         mContext = context;
         mChangeReporter = new ChangeReporter(
                 StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
+        mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext);
+    }
+
+    @VisibleForTesting
+    PlatformCompat(Context context, CompatConfig compatConfig) {
+        mContext = context;
+        mChangeReporter = new ChangeReporter(
+                StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
+        mCompatConfig = compatConfig;
     }
 
     @Override
@@ -75,7 +88,7 @@
 
     @Override
     public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
-        if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) {
+        if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
             reportChange(changeId, appInfo.uid,
                     StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
             return true;
@@ -122,57 +135,59 @@
      * otherwise.
      */
     public boolean registerListener(long changeId, CompatChange.ChangeListener listener) {
-        return CompatConfig.get().registerListener(changeId, listener);
+        return mCompatConfig.registerListener(changeId, listener);
     }
 
     @Override
-    public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
-        CompatConfig.get().addOverrides(overrides, packageName);
+    public void setOverrides(CompatibilityChangeConfig overrides, String packageName)
+            throws RemoteException, SecurityException {
+        mCompatConfig.addOverrides(overrides, packageName);
         killPackage(packageName);
     }
 
     @Override
-    public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) {
-        CompatConfig.get().addOverrides(overrides, packageName);
+    public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName)
+            throws RemoteException, SecurityException {
+        mCompatConfig.addOverrides(overrides, packageName);
     }
 
     @Override
-    public void clearOverrides(String packageName) {
-        CompatConfig config = CompatConfig.get();
-        config.removePackageOverrides(packageName);
+    public void clearOverrides(String packageName) throws RemoteException, SecurityException {
+        mCompatConfig.removePackageOverrides(packageName);
         killPackage(packageName);
     }
 
     @Override
-    public void clearOverridesForTest(String packageName) {
-        CompatConfig config = CompatConfig.get();
-        config.removePackageOverrides(packageName);
+    public void clearOverridesForTest(String packageName)
+            throws RemoteException, SecurityException {
+        mCompatConfig.removePackageOverrides(packageName);
     }
 
     @Override
-    public boolean clearOverride(long changeId, String packageName) {
-        boolean existed = CompatConfig.get().removeOverride(changeId, packageName);
+    public boolean clearOverride(long changeId, String packageName)
+            throws RemoteException, SecurityException {
+        boolean existed = mCompatConfig.removeOverride(changeId, packageName);
         killPackage(packageName);
         return existed;
     }
 
     @Override
     public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) {
-        return CompatConfig.get().getAppConfig(appInfo);
+        return mCompatConfig.getAppConfig(appInfo);
     }
 
     @Override
     public CompatibilityChangeInfo[] listAllChanges() {
-        return CompatConfig.get().dumpChanges();
+        return mCompatConfig.dumpChanges();
     }
 
     /**
      * Check whether the change is known to the compat config.
-     * @param changeId
+     *
      * @return {@code true} if the change is known.
      */
     public boolean isKnownChangeId(long changeId) {
-        return CompatConfig.get().isKnownChangeId(changeId);
+        return mCompatConfig.isKnownChangeId(changeId);
 
     }
 
@@ -182,11 +197,11 @@
      *
      * @param appInfo The app in question
      * @return A sorted long array of change IDs. We use a primitive array to minimize memory
-     *      footprint: Every app process will store this array statically so we aim to reduce
-     *      overhead as much as possible.
+     * footprint: Every app process will store this array statically so we aim to reduce
+     * overhead as much as possible.
      */
     public long[] getDisabledChanges(ApplicationInfo appInfo) {
-        return CompatConfig.get().getDisabledChanges(appInfo);
+        return mCompatConfig.getDisabledChanges(appInfo);
     }
 
     /**
@@ -196,18 +211,24 @@
      * @return The change ID, or {@code -1} if no change with that name exists.
      */
     public long lookupChangeId(String name) {
-        return CompatConfig.get().lookupChangeId(name);
+        return mCompatConfig.lookupChangeId(name);
     }
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
-        CompatConfig.get().dumpConfig(pw);
+        mCompatConfig.dumpConfig(pw);
+    }
+
+    @Override
+    public IOverrideValidator getOverrideValidator() {
+        return mCompatConfig.getOverrideValidator();
     }
 
     /**
      * Clears information stored about events reported on behalf of an app.
      * To be called once upon app start or end. A second call would be a no-op.
+     *
      * @param appInfo the app to reset
      */
     public void resetReporting(ApplicationInfo appInfo) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e39d6d5..d7ae5b5 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1305,13 +1305,21 @@
     }
 
     private SurfaceControl.ScreenshotGraphicBuffer screenshotInternal(int displayId) {
-        final IBinder token = getDisplayToken(displayId);
-        if (token == null) {
-            return null;
+        synchronized (mSyncRoot) {
+            final IBinder token = getDisplayToken(displayId);
+            if (token == null) {
+                return null;
+            }
+            final LogicalDisplay logicalDisplay = mLogicalDisplays.get(displayId);
+            if (logicalDisplay == null) {
+                return null;
+            }
+
+            final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked();
+            return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, new Rect(),
+                    displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(),
+                    false /* useIdentityTransform */, 0 /* rotation */);
         }
-        return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(
-                        token, new Rect(), 0 /* width */, 0 /* height */,
-                        false /* useIdentityTransform */, 0 /* rotation */);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index ad728c1..8498dcb 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -237,15 +237,15 @@
                 lowestConsideredPriority++;
             }
 
-            int defaultModeId = defaultMode.getModeId();
+            int baseModeId = defaultMode.getModeId();
             if (availableModes.length > 0) {
-                defaultModeId = availableModes[0];
+                baseModeId = availableModes[0];
             }
             // filterModes function is going to filter the modes based on the voting system. If
             // the application requests a given mode with preferredModeId function, it will be
-            // stored as defaultModeId.
+            // stored as baseModeId.
             return new DesiredDisplayModeSpecs(
-                    defaultModeId, new RefreshRateRange(minRefreshRate, maxRefreshRate));
+                    baseModeId, new RefreshRateRange(minRefreshRate, maxRefreshRate));
         }
     }
 
@@ -526,7 +526,7 @@
     }
 
     /**
-     * Information about the desired display mode to be set by the system. Includes the default
+     * Information about the desired display mode to be set by the system. Includes the base
      * mode ID and refresh rate range.
      *
      * We have this class in addition to SurfaceControl.DesiredDisplayConfigSpecs to make clear the
@@ -535,10 +535,10 @@
      */
     public static final class DesiredDisplayModeSpecs {
         /**
-         * Default mode ID. This is what system defaults to for all other settings, or
+         * Base mode ID. This is what system defaults to for all other settings, or
          * if the refresh rate range is not available.
          */
-        public int defaultModeId;
+        public int baseModeId;
         /**
          * The refresh rate range.
          */
@@ -548,9 +548,8 @@
             refreshRateRange = new RefreshRateRange();
         }
 
-        public DesiredDisplayModeSpecs(
-                int defaultModeId, @NonNull RefreshRateRange refreshRateRange) {
-            this.defaultModeId = defaultModeId;
+        public DesiredDisplayModeSpecs(int baseModeId, @NonNull RefreshRateRange refreshRateRange) {
+            this.baseModeId = baseModeId;
             this.refreshRateRange = refreshRateRange;
         }
 
@@ -559,7 +558,7 @@
          */
         @Override
         public String toString() {
-            return String.format("defaultModeId=%d min=%.0f max=%.0f", defaultModeId,
+            return String.format("baseModeId=%d min=%.0f max=%.0f", baseModeId,
                     refreshRateRange.min, refreshRateRange.max);
         }
         /**
@@ -577,7 +576,7 @@
 
             DesiredDisplayModeSpecs desiredDisplayModeSpecs = (DesiredDisplayModeSpecs) other;
 
-            if (defaultModeId != desiredDisplayModeSpecs.defaultModeId) {
+            if (baseModeId != desiredDisplayModeSpecs.baseModeId) {
                 return false;
             }
             if (!refreshRateRange.equals(desiredDisplayModeSpecs.refreshRateRange)) {
@@ -588,14 +587,14 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(defaultModeId, refreshRateRange);
+            return Objects.hash(baseModeId, refreshRateRange);
         }
 
         /**
          * Copy values from the other object.
          */
         public void copyFrom(DesiredDisplayModeSpecs other) {
-            defaultModeId = other.defaultModeId;
+            baseModeId = other.baseModeId;
             refreshRateRange.min = other.refreshRateRange.min;
             refreshRateRange.max = other.refreshRateRange.max;
         }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index cf94d46..fb8a419 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -272,14 +272,14 @@
 
             // Check whether surface flinger spontaneously changed display config specs out from
             // under us. If so, schedule a traversal to reapply our display config specs.
-            if (mDisplayModeSpecs.defaultModeId != 0) {
-                int activeDefaultMode =
+            if (mDisplayModeSpecs.baseModeId != 0) {
+                int activeBaseMode =
                         findMatchingModeIdLocked(physicalDisplayConfigSpecs.defaultConfig);
                 // If we can't map the defaultConfig index to a mode, then the physical display
                 // configs must have changed, and the code below for handling changes to the
                 // list of available modes will take care of updating display config specs.
-                if (activeDefaultMode != 0) {
-                    if (mDisplayModeSpecs.defaultModeId != activeDefaultMode
+                if (activeBaseMode != 0) {
+                    if (mDisplayModeSpecs.baseModeId != activeBaseMode
                             || mDisplayModeSpecs.refreshRateRange.min
                                     != physicalDisplayConfigSpecs.minRefreshRate
                             || mDisplayModeSpecs.refreshRateRange.max
@@ -312,14 +312,14 @@
                 mDefaultModeId = activeRecord.mMode.getModeId();
             }
 
-            // Determine whether the display mode specs' default mode is still there.
-            if (mSupportedModes.indexOfKey(mDisplayModeSpecs.defaultModeId) < 0) {
-                if (mDisplayModeSpecs.defaultModeId != 0) {
+            // Determine whether the display mode specs' base mode is still there.
+            if (mSupportedModes.indexOfKey(mDisplayModeSpecs.baseModeId) < 0) {
+                if (mDisplayModeSpecs.baseModeId != 0) {
                     Slog.w(TAG,
-                            "DisplayModeSpecs default mode no longer available, using currently"
-                                    + " active mode as default.");
+                            "DisplayModeSpecs base mode no longer available, using currently"
+                                    + " active mode.");
                 }
-                mDisplayModeSpecs.defaultModeId = activeRecord.mMode.getModeId();
+                mDisplayModeSpecs.baseModeId = activeRecord.mMode.getModeId();
                 mDisplayModeSpecsInvalid = true;
             }
 
@@ -648,13 +648,13 @@
         @Override
         public void setDesiredDisplayModeSpecsLocked(
                 DisplayModeDirector.DesiredDisplayModeSpecs displayModeSpecs) {
-            if (displayModeSpecs.defaultModeId == 0) {
+            if (displayModeSpecs.baseModeId == 0) {
                 // Bail if the caller is requesting a null mode. We'll get called again shortly with
                 // a valid mode.
                 return;
             }
-            int defaultPhysIndex = findDisplayInfoIndexLocked(displayModeSpecs.defaultModeId);
-            if (defaultPhysIndex < 0) {
+            int basePhysIndex = findDisplayInfoIndexLocked(displayModeSpecs.baseModeId);
+            if (basePhysIndex < 0) {
                 // When a display is hotplugged, it's possible for a mode to be removed that was
                 // previously valid. Because of the way display changes are propagated through the
                 // framework, and the caching of the display mode specs in LogicalDisplay, it's
@@ -662,8 +662,7 @@
                 // mode. This should only happen in extremely rare cases. A followup call will
                 // contain a valid mode id.
                 Slog.w(TAG,
-                        "Ignoring request for invalid default mode id "
-                                + displayModeSpecs.defaultModeId);
+                        "Ignoring request for invalid base mode id " + displayModeSpecs.baseModeId);
                 updateDeviceInfoLocked();
                 return;
             }
@@ -673,7 +672,7 @@
                 getHandler().sendMessage(PooledLambda.obtainMessage(
                         LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
                         getDisplayTokenLocked(),
-                        new SurfaceControl.DesiredDisplayConfigSpecs(defaultPhysIndex,
+                        new SurfaceControl.DesiredDisplayConfigSpecs(basePhysIndex,
                                 mDisplayModeSpecs.refreshRateRange.min,
                                 mDisplayModeSpecs.refreshRateRange.max)));
             }
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index b6255d1..c8e5f6c 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -317,7 +317,7 @@
         @Override
         public void setDesiredDisplayModeSpecsLocked(
                 DisplayModeDirector.DesiredDisplayModeSpecs displayModeSpecs) {
-            final int id = displayModeSpecs.defaultModeId;
+            final int id = displayModeSpecs.baseModeId;
             int index = -1;
             if (id == 0) {
                 // Use the default.
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index ae6d63a..90358ca 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -83,11 +83,11 @@
 import android.widget.Toast;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
@@ -186,6 +186,9 @@
     // The associations of input devices to displays by port. Maps from input device port (String)
     // to display id (int). Currently only accessed by InputReader.
     private final Map<String, Integer> mStaticAssociations;
+    private final Object mAssociationsLock = new Object();
+    @GuardedBy("mAssociationLock")
+    private final Map<String, Integer> mRuntimeAssociations = new HashMap<String, Integer>();
 
     private static native long nativeInit(InputManagerService service,
             Context context, MessageQueue messageQueue);
@@ -240,6 +243,7 @@
     private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon);
     private static native void nativeSetPointerCapture(long ptr, boolean detached);
     private static native boolean nativeCanDispatchToDisplay(long ptr, int deviceId, int displayId);
+    private static native void nativeNotifyPortAssociationsChanged(long ptr);
 
     // Input event injection constants defined in InputDispatcher.h.
     private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
@@ -1723,6 +1727,49 @@
         nativeSetCustomPointerIcon(mPtr, icon);
     }
 
+    /**
+     * Add a runtime association between the input port and the display port. This overrides any
+     * static associations.
+     * @param inputPort The port of the input device.
+     * @param displayPort The physical port of the associated display.
+     */
+    @Override // Binder call
+    public void addPortAssociation(@NonNull String inputPort, int displayPort) {
+        if (!checkCallingPermission(
+                android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT,
+                "addPortAssociation()")) {
+            throw new SecurityException(
+                    "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT permission");
+        }
+
+        Objects.requireNonNull(inputPort);
+        synchronized (mAssociationsLock) {
+            mRuntimeAssociations.put(inputPort, displayPort);
+        }
+        nativeNotifyPortAssociationsChanged(mPtr);
+    }
+
+    /**
+     * Remove the runtime association between the input port and the display port. Any existing
+     * static association for the cleared input port will be restored.
+     * @param inputPort The port of the input device to be cleared.
+     */
+    @Override // Binder call
+    public void removePortAssociation(@NonNull String inputPort) {
+        if (!checkCallingPermission(
+                android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT,
+                "clearPortAssociations()")) {
+            throw new SecurityException(
+                    "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT permission");
+        }
+
+        Objects.requireNonNull(inputPort);
+        synchronized (mAssociationsLock) {
+            mRuntimeAssociations.remove(inputPort);
+        }
+        nativeNotifyPortAssociationsChanged(mPtr);
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -1743,6 +1790,16 @@
                 pw.println("  display: " + v);
             });
         }
+
+        synchronized (mAssociationsLock) {
+            if (!mRuntimeAssociations.isEmpty()) {
+                pw.println("Runtime Associations:");
+                mRuntimeAssociations.forEach((k, v) -> {
+                    pw.print("  port: " + k);
+                    pw.println("  display: " + v);
+                });
+            }
+        }
     }
 
     private boolean checkCallingPermission(String permission, String func) {
@@ -1766,6 +1823,7 @@
     @Override
     public void monitor() {
         synchronized (mInputFilterLock) { }
+        synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */}
         nativeMonitor(mPtr);
     }
 
@@ -1930,7 +1988,7 @@
      * @return Flattened list
      */
     private static List<String> flatten(@NonNull Map<String, Integer> map) {
-        List<String> list = new ArrayList<>(map.size() * 2);
+        final List<String> list = new ArrayList<>(map.size() * 2);
         map.forEach((k, v)-> {
             list.add(k);
             list.add(v.toString());
@@ -1943,11 +2001,11 @@
      * directory.
      */
     private static Map<String, Integer> loadStaticInputPortAssociations() {
-        File baseDir = Environment.getVendorDirectory();
-        File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH);
+        final File baseDir = Environment.getVendorDirectory();
+        final File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH);
 
         try {
-            InputStream stream = new FileInputStream(confFile);
+            final InputStream stream = new FileInputStream(confFile);
             return ConfigurationProcessor.processInputPortAssociations(stream);
         } catch (FileNotFoundException e) {
             // Most of the time, file will not exist, which is expected.
@@ -1960,7 +2018,14 @@
 
     // Native callback
     private String[] getInputPortAssociations() {
-        List<String> associationList = flatten(mStaticAssociations);
+        final Map<String, Integer> associations = new HashMap<>(mStaticAssociations);
+
+        // merge the runtime associations.
+        synchronized (mAssociationsLock) {
+            associations.putAll(mRuntimeAssociations);
+        }
+
+        final List<String> associationList = flatten(associations);
         return associationList.toArray(new String[0]);
     }
 
diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
index f90fab4..31d4816 100644
--- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java
+++ b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
@@ -25,6 +25,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.integrity.model.RuleMetadata;
 import com.android.server.integrity.parser.RuleBinaryParser;
+import com.android.server.integrity.parser.RuleIndexRange;
 import com.android.server.integrity.parser.RuleIndexingController;
 import com.android.server.integrity.parser.RuleMetadataParser;
 import com.android.server.integrity.parser.RuleParseException;
@@ -152,7 +153,7 @@
             throws IOException, RuleParseException {
         synchronized (RULES_LOCK) {
             // Try to identify indexes from the index file.
-            List<List<Integer>> ruleReadingIndexes =
+            List<RuleIndexRange> ruleReadingIndexes =
                     mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata);
 
             // Read the rules based on the index information.
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
new file mode 100644
index 0000000..8c8450e
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
@@ -0,0 +1,50 @@
+/*
+ * 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.server.integrity.parser;
+
+import android.annotation.Nullable;
+
+/**
+ * A wrapper class to represent an indexing range that is identified by the {@link
+ * RuleIndexingController}.
+ */
+public class RuleIndexRange {
+    private static int sStartIndex;
+    private static int sEndIndex;
+
+    /** Constructor with start and end indexes. */
+    public RuleIndexRange(int startIndex, int endIndex) {
+        this.sStartIndex = startIndex;
+        this.sEndIndex = endIndex;
+    }
+
+    /** Returns the startIndex. */
+    public int getStartIndex() {
+        return sStartIndex;
+    }
+
+    /** Returns the end index. */
+    public int getEndIndex() {
+        return sEndIndex;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object object) {
+        return sStartIndex == ((RuleIndexRange) object).getStartIndex()
+                && sEndIndex == ((RuleIndexRange) object).getEndIndex();
+    }
+}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
index b642fa6..c971322 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
@@ -17,6 +17,7 @@
 package com.android.server.integrity.parser;
 
 import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
+import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
 import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
 import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
 
@@ -24,25 +25,27 @@
 
 import com.android.server.integrity.model.BitInputStream;
 
-import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
 
 /** Helper class to identify the necessary indexes that needs to be read. */
 public class RuleIndexingController {
 
-    private static TreeMap<String, Integer> sPackageNameBasedIndexes;
-    private static TreeMap<String, Integer> sAppCertificateBasedIndexes;
-    private static TreeMap<String, Integer> sUnindexedRuleIndexes;
+    private static LinkedHashMap<String, Integer> sPackageNameBasedIndexes;
+    private static LinkedHashMap<String, Integer> sAppCertificateBasedIndexes;
+    private static LinkedHashMap<String, Integer> sUnindexedRuleIndexes;
 
     /**
      * Provide the indexing file to read and the object will be constructed by reading and
      * identifying the indexes.
      */
-    public RuleIndexingController(FileInputStream fileInputStream) throws IOException {
-        BitInputStream bitInputStream = new BitInputStream(fileInputStream);
+    public RuleIndexingController(InputStream inputStream) throws IOException {
+        BitInputStream bitInputStream = new BitInputStream(inputStream);
         sPackageNameBasedIndexes = getNextIndexGroup(bitInputStream);
         sAppCertificateBasedIndexes = getNextIndexGroup(bitInputStream);
         sUnindexedRuleIndexes = getNextIndexGroup(bitInputStream);
@@ -52,24 +55,57 @@
      * Returns a list of integers with the starting and ending bytes of the rules that needs to be
      * read and evaluated.
      */
-    public List<List<Integer>> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
-        // TODO(b/145493956): Identify and return the indexes that needs to be read.
-        return new ArrayList<>();
+    public List<RuleIndexRange> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
+        ArrayList<RuleIndexRange> indexRanges = new ArrayList();
+
+        // Add the range for package name indexes rules.
+        indexRanges.add(
+                searchIndexingKeysRangeContainingKey(
+                        sPackageNameBasedIndexes, appInstallMetadata.getPackageName()));
+
+        // Add the range for app certificate indexes rules.
+        indexRanges.add(
+                searchIndexingKeysRangeContainingKey(
+                        sAppCertificateBasedIndexes, appInstallMetadata.getAppCertificate()));
+
+        // Add the range for unindexed rules.
+        indexRanges.add(
+                new RuleIndexRange(
+                        sUnindexedRuleIndexes.get(START_INDEXING_KEY),
+                        sUnindexedRuleIndexes.get(END_INDEXING_KEY)));
+
+        return indexRanges;
     }
 
-    private TreeMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream)
+    private LinkedHashMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream)
             throws IOException {
-        TreeMap<String, Integer> keyToIndexMap = new TreeMap<>();
+        LinkedHashMap<String, Integer> keyToIndexMap = new LinkedHashMap<>();
         while (bitInputStream.hasNext()) {
             String key = getStringValue(bitInputStream);
             int value = getIntValue(bitInputStream);
 
             keyToIndexMap.put(key, value);
 
-            if (key == END_INDEXING_KEY) {
+            if (key.matches(END_INDEXING_KEY)) {
                 break;
             }
         }
         return keyToIndexMap;
     }
+
+    private RuleIndexRange searchIndexingKeysRangeContainingKey(
+            LinkedHashMap<String, Integer> indexMap, String searchedKey) {
+        TreeSet<String> keyTreeSet =
+                indexMap.keySet().stream()
+                        .filter(key -> !key.matches(START_INDEXING_KEY) && !key.matches(
+                                END_INDEXING_KEY))
+                        .collect(Collectors.toCollection(TreeSet::new));
+
+        String minIndex = keyTreeSet.floor(searchedKey);
+        String maxIndex = keyTreeSet.ceiling(searchedKey);
+
+        return new RuleIndexRange(
+                indexMap.get(minIndex == null ? START_INDEXING_KEY : minIndex),
+                indexMap.get(maxIndex == null ? END_INDEXING_KEY : maxIndex));
+    }
 }
diff --git a/services/core/java/com/android/server/location/LocationRequestStatistics.java b/services/core/java/com/android/server/location/LocationRequestStatistics.java
index b7ccb26..45c8334 100644
--- a/services/core/java/com/android/server/location/LocationRequestStatistics.java
+++ b/services/core/java/com/android/server/location/LocationRequestStatistics.java
@@ -1,8 +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.
+ */
+
 package com.android.server.location;
 
 import android.os.SystemClock;
 import android.util.Log;
+import android.util.TimeUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.util.ArrayList;
 import java.util.HashMap;
 
 /**
@@ -17,6 +38,8 @@
     public final HashMap<PackageProviderKey, PackageStatistics> statistics
             = new HashMap<PackageProviderKey, PackageStatistics>();
 
+    public final RequestSummaryLimitedHistory history = new RequestSummaryLimitedHistory();
+
     /**
      * Signals that a package has started requesting locations.
      *
@@ -34,6 +57,7 @@
         }
         stats.startRequesting(intervalMs);
         stats.updateForeground(isForeground);
+        history.addRequest(packageName, providerName, intervalMs);
     }
 
     /**
@@ -48,6 +72,7 @@
         if (stats != null) {
             stats.stopRequesting();
         }
+        history.removeRequest(packageName, providerName);
     }
 
     /**
@@ -77,7 +102,7 @@
          */
         public final String providerName;
 
-        public PackageProviderKey(String packageName, String providerName) {
+        PackageProviderKey(String packageName, String providerName) {
             this.packageName = packageName;
             this.providerName = providerName;
         }
@@ -100,6 +125,104 @@
     }
 
     /**
+     * A data structure to hold past requests
+     */
+    public static class RequestSummaryLimitedHistory {
+        @VisibleForTesting
+        static final int MAX_SIZE = 100;
+
+        final ArrayList<RequestSummary> mList = new ArrayList<>(MAX_SIZE);
+
+        /**
+         * Append an added location request to the history
+         */
+        @VisibleForTesting
+        void addRequest(String packageName, String providerName, long intervalMs) {
+            addRequestSummary(new RequestSummary(packageName, providerName, intervalMs));
+        }
+
+        /**
+         * Append a removed location request to the history
+         */
+        @VisibleForTesting
+        void removeRequest(String packageName, String providerName) {
+            addRequestSummary(new RequestSummary(
+                    packageName, providerName, RequestSummary.REQUEST_ENDED_INTERVAL));
+        }
+
+        private void addRequestSummary(RequestSummary summary) {
+            while (mList.size() >= MAX_SIZE) {
+                mList.remove(0);
+            }
+            mList.add(summary);
+        }
+
+        /**
+         * Dump history to a printwriter (for dumpsys location)
+         */
+        public void dump(IndentingPrintWriter ipw) {
+            long systemElapsedOffsetMillis = System.currentTimeMillis()
+                    - SystemClock.elapsedRealtime();
+
+            ipw.println("Last Several Location Requests:");
+            ipw.increaseIndent();
+
+            for (RequestSummary requestSummary : mList) {
+                requestSummary.dump(ipw, systemElapsedOffsetMillis);
+            }
+
+            ipw.decreaseIndent();
+        }
+    }
+
+    /**
+     * A data structure to hold a single request
+     */
+    static class RequestSummary {
+        /**
+         * Name of package requesting location.
+         */
+        private final String mPackageName;
+        /**
+         * Name of provider being requested (e.g. "gps").
+         */
+        private final String mProviderName;
+        /**
+         * Interval Requested, or REQUEST_ENDED_INTERVAL indicating request has ended
+         */
+        private final long mIntervalMillis;
+        /**
+         * Elapsed time of request
+         */
+        private final long mElapsedRealtimeMillis;
+
+        /**
+         * Placeholder for requested ending (other values indicate request started/changed)
+         */
+        static final long REQUEST_ENDED_INTERVAL = -1;
+
+        RequestSummary(String packageName, String providerName, long intervalMillis) {
+            this.mPackageName = packageName;
+            this.mProviderName = providerName;
+            this.mIntervalMillis = intervalMillis;
+            this.mElapsedRealtimeMillis = SystemClock.elapsedRealtime();
+        }
+
+        void dump(IndentingPrintWriter ipw, long systemElapsedOffsetMillis) {
+            StringBuilder s = new StringBuilder();
+            long systemTimeMillis = systemElapsedOffsetMillis + mElapsedRealtimeMillis;
+            s.append("At ").append(TimeUtils.formatForLogging(systemTimeMillis)).append(": ")
+                    .append(mIntervalMillis == REQUEST_ENDED_INTERVAL ? "- " : "+ ")
+                    .append(String.format("%7s", mProviderName)).append(" request from ")
+                    .append(mPackageName);
+            if (mIntervalMillis != REQUEST_ENDED_INTERVAL) {
+                s.append(" at interval ").append(mIntervalMillis / 1000).append(" seconds");
+            }
+            ipw.println(s);
+        }
+    }
+
+    /**
      * Usage statistics for a package/provider pair.
      */
     public static class PackageStatistics {
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 115155c..9c9a412 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -20,7 +20,6 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Intent;
-import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
 import android.media.RouteSessionInfo;
 
@@ -47,16 +46,16 @@
     }
 
     public abstract void requestCreateSession(String packageName, String routeId,
-            String controlCategory, long requestId);
-    public abstract void releaseSession(int sessionId);
+            String routeType, long requestId);
+    public abstract void releaseSession(String sessionId);
 
-    public abstract void selectRoute(int sessionId, MediaRoute2Info route);
-    public abstract void deselectRoute(int sessionId, MediaRoute2Info route);
-    public abstract void transferToRoute(int sessionId, MediaRoute2Info route);
+    public abstract void selectRoute(String sessionId, String routeId);
+    public abstract void deselectRoute(String sessionId, String routeId);
+    public abstract void transferToRoute(String sessionId, String routeId);
 
-    public abstract void sendControlRequest(MediaRoute2Info route, Intent request);
-    public abstract void requestSetVolume(MediaRoute2Info route, int volume);
-    public abstract void requestUpdateVolume(MediaRoute2Info route, int delta);
+    public abstract void sendControlRequest(String routeId, Intent request);
+    public abstract void requestSetVolume(String routeId, int volume);
+    public abstract void requestUpdateVolume(String routeId, int delta);
 
     @NonNull
     public String getUniqueId() {
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index f8d8f9f..6359835 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -24,7 +24,6 @@
 import android.content.ServiceConnection;
 import android.media.IMediaRoute2Provider;
 import android.media.IMediaRoute2ProviderClient;
-import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRoute2ProviderService;
 import android.media.RouteSessionInfo;
@@ -77,17 +76,17 @@
     }
 
     @Override
-    public void requestCreateSession(String packageName, String routeId, String controlCategory,
+    public void requestCreateSession(String packageName, String routeId, String routeType,
             long requestId) {
         if (mConnectionReady) {
-            mActiveConnection.requestCreateSession(packageName, routeId, controlCategory,
+            mActiveConnection.requestCreateSession(packageName, routeId, routeType,
                     requestId);
             updateBinding();
         }
     }
 
     @Override
-    public void releaseSession(int sessionId) {
+    public void releaseSession(String sessionId) {
         if (mConnectionReady) {
             mActiveConnection.releaseSession(sessionId);
             updateBinding();
@@ -95,46 +94,46 @@
     }
 
     @Override
-    public void selectRoute(int sessionId, MediaRoute2Info route) {
+    public void selectRoute(String sessionId, String routeId) {
         if (mConnectionReady) {
-            mActiveConnection.selectRoute(sessionId, route.getId());
+            mActiveConnection.selectRoute(sessionId, routeId);
         }
     }
 
     @Override
-    public void deselectRoute(int sessionId, MediaRoute2Info route) {
+    public void deselectRoute(String sessionId, String routeId) {
         if (mConnectionReady) {
-            mActiveConnection.deselectRoute(sessionId, route.getId());
+            mActiveConnection.deselectRoute(sessionId, routeId);
         }
     }
 
     @Override
-    public void transferToRoute(int sessionId, MediaRoute2Info route) {
+    public void transferToRoute(String sessionId, String routeId) {
         if (mConnectionReady) {
-            mActiveConnection.transferToRoute(sessionId, route.getId());
+            mActiveConnection.transferToRoute(sessionId, routeId);
         }
     }
 
     @Override
-    public void sendControlRequest(MediaRoute2Info route, Intent request) {
+    public void sendControlRequest(String routeId, Intent request) {
         if (mConnectionReady) {
-            mActiveConnection.sendControlRequest(route.getId(), request);
+            mActiveConnection.sendControlRequest(routeId, request);
             updateBinding();
         }
     }
 
     @Override
-    public void requestSetVolume(MediaRoute2Info route, int volume) {
+    public void requestSetVolume(String routeId, int volume) {
         if (mConnectionReady) {
-            mActiveConnection.requestSetVolume(route.getId(), volume);
+            mActiveConnection.requestSetVolume(routeId, volume);
             updateBinding();
         }
     }
 
     @Override
-    public void requestUpdateVolume(MediaRoute2Info route, int delta) {
+    public void requestUpdateVolume(String routeId, int delta) {
         if (mConnectionReady) {
-            mActiveConnection.requestUpdateVolume(route.getId(), delta);
+            mActiveConnection.requestUpdateVolume(routeId, delta);
             updateBinding();
         }
     }
@@ -303,6 +302,11 @@
                     + mComponentName);
             return;
         }
+
+        sessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+                .setProviderId(getUniqueId())
+                .build();
+
         mCallback.onSessionInfoChanged(this, sessionInfo);
     }
 
@@ -346,17 +350,17 @@
             mClient.dispose();
         }
 
-        public void requestCreateSession(String packageName, String routeId, String controlCategory,
+        public void requestCreateSession(String packageName, String routeId, String routeType,
                 long requestId) {
             try {
                 mProvider.requestCreateSession(packageName, routeId,
-                        controlCategory, requestId);
+                        routeType, requestId);
             } catch (RemoteException ex) {
                 Slog.e(TAG, "Failed to deliver request to create a session.", ex);
             }
         }
 
-        public void releaseSession(int sessionId) {
+        public void releaseSession(String sessionId) {
             try {
                 mProvider.releaseSession(sessionId);
             } catch (RemoteException ex) {
@@ -364,7 +368,7 @@
             }
         }
 
-        public void selectRoute(int sessionId, String routeId) {
+        public void selectRoute(String sessionId, String routeId) {
             try {
                 mProvider.selectRoute(sessionId, routeId);
             } catch (RemoteException ex) {
@@ -372,7 +376,7 @@
             }
         }
 
-        public void deselectRoute(int sessionId, String routeId) {
+        public void deselectRoute(String sessionId, String routeId) {
             try {
                 mProvider.deselectRoute(sessionId, routeId);
             } catch (RemoteException ex) {
@@ -380,7 +384,7 @@
             }
         }
 
-        public void transferToRoute(int sessionId, String routeId) {
+        public void transferToRoute(String sessionId, String routeId) {
             try {
                 mProvider.transferToRoute(sessionId, routeId);
             } catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 562a720..487ab52 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -16,6 +16,9 @@
 
 package com.android.server.media;
 
+import static android.media.MediaRouter2Utils.getOriginalId;
+import static android.media.MediaRouter2Utils.getProviderId;
+
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.annotation.NonNull;
@@ -28,6 +31,7 @@
 import android.media.IMediaRouter2Manager;
 import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
+import android.media.RouteDiscoveryRequest;
 import android.media.RouteSessionInfo;
 import android.os.Binder;
 import android.os.Bundle;
@@ -174,18 +178,18 @@
     }
 
     public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
-            String controlCategory, int requestId) {
+            String routeType, int requestId) {
         Objects.requireNonNull(client, "client must not be null");
         Objects.requireNonNull(route, "route must not be null");
-        if (TextUtils.isEmpty(controlCategory)) {
-            throw new IllegalArgumentException("controlCategory must not be empty");
+        if (TextUtils.isEmpty(routeType)) {
+            throw new IllegalArgumentException("routeType must not be empty");
         }
 
         final long token = Binder.clearCallingIdentity();
 
         try {
             synchronized (mLock) {
-                requestCreateSessionLocked(client, route, controlCategory, requestId);
+                requestCreateSessionLocked(client, route, routeType, requestId);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -196,6 +200,9 @@
             MediaRoute2Info route) {
         Objects.requireNonNull(client, "client must not be null");
         Objects.requireNonNull(route, "route must not be null");
+        if (TextUtils.isEmpty(uniqueSessionId)) {
+            throw new IllegalArgumentException("uniqueSessionId must not be empty");
+        }
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -212,6 +219,9 @@
             MediaRoute2Info route) {
         Objects.requireNonNull(client, "client must not be null");
         Objects.requireNonNull(route, "route must not be null");
+        if (TextUtils.isEmpty(uniqueSessionId)) {
+            throw new IllegalArgumentException("uniqueSessionId must not be empty");
+        }
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -227,6 +237,9 @@
             MediaRoute2Info route) {
         Objects.requireNonNull(client, "client must not be null");
         Objects.requireNonNull(route, "route must not be null");
+        if (TextUtils.isEmpty(uniqueSessionId)) {
+            throw new IllegalArgumentException("uniqueSessionId must not be empty");
+        }
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -240,6 +253,9 @@
 
     public void releaseSession(IMediaRouter2Client client, String uniqueSessionId) {
         Objects.requireNonNull(client, "client must not be null");
+        if (TextUtils.isEmpty(uniqueSessionId)) {
+            throw new IllegalArgumentException("uniqueSessionId must not be empty");
+        }
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -267,16 +283,16 @@
         }
     }
 
-    public void setControlCategories(@NonNull IMediaRouter2Client client,
-            @NonNull List<String> categories) {
+    public void setDiscoveryRequest2(@NonNull IMediaRouter2Client client,
+            @NonNull RouteDiscoveryRequest request) {
         Objects.requireNonNull(client, "client must not be null");
-        Objects.requireNonNull(categories, "categories must not be null");
+        Objects.requireNonNull(request, "request must not be null");
 
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
                 Client2Record clientRecord = mAllClientRecords.get(client.asBinder());
-                setControlCategoriesLocked(clientRecord, categories);
+                setDiscoveryRequestLocked(clientRecord, request);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -434,7 +450,7 @@
     }
 
     private void requestCreateSessionLocked(@NonNull IMediaRouter2Client client,
-            @NonNull MediaRoute2Info route, @NonNull String controlCategory, long requestId) {
+            @NonNull MediaRoute2Info route, @NonNull String routeType, long requestId) {
         final IBinder binder = client.asBinder();
         final Client2Record clientRecord = mAllClientRecords.get(binder);
 
@@ -447,7 +463,7 @@
             clientRecord.mUserRecord.mHandler.sendMessage(
                     obtainMessage(UserHandler::requestCreateSessionOnHandler,
                             clientRecord.mUserRecord.mHandler,
-                            clientRecord, route, controlCategory, requestId));
+                            clientRecord, route, routeType, requestId));
         }
     }
 
@@ -502,13 +518,14 @@
         }
     }
 
-    private void setControlCategoriesLocked(Client2Record clientRecord, List<String> categories) {
+    private void setDiscoveryRequestLocked(Client2Record clientRecord,
+            RouteDiscoveryRequest discoveryRequest) {
         if (clientRecord != null) {
-            if (clientRecord.mControlCategories.equals(categories)) {
+            if (clientRecord.mDiscoveryRequest.equals(discoveryRequest)) {
                 return;
             }
 
-            clientRecord.mControlCategories = categories;
+            clientRecord.mDiscoveryRequest = discoveryRequest;
             clientRecord.mUserRecord.mHandler.sendMessage(
                     obtainMessage(UserHandler::updateClientUsage,
                             clientRecord.mUserRecord.mHandler, clientRecord));
@@ -605,9 +622,9 @@
             }
             long uniqueRequestId = toUniqueRequestId(managerRecord.mClientId, requestId);
             if (clientRecord != null && managerRecord.mTrusted) {
-                //TODO: select category properly
+                //TODO: select route type properly
                 requestCreateSessionLocked(clientRecord.mClient, route,
-                        route.getSupportedCategories().get(0), uniqueRequestId);
+                        route.getRouteTypes().get(0), uniqueRequestId);
             }
         }
     }
@@ -725,7 +742,7 @@
         public final boolean mTrusted;
         public final int mClientId;
 
-        public List<String> mControlCategories;
+        public RouteDiscoveryRequest mDiscoveryRequest;
         public boolean mIsManagerSelecting;
         public MediaRoute2Info mSelectingRoute;
         public MediaRoute2Info mSelectedRoute;
@@ -735,7 +752,7 @@
             mUserRecord = userRecord;
             mPackageName = packageName;
             mSelectRouteSequenceNumbers = new ArrayList<>();
-            mControlCategories = Collections.emptyList();
+            mDiscoveryRequest = RouteDiscoveryRequest.EMPTY;
             mClient = client;
             mUid = uid;
             mPid = pid;
@@ -961,7 +978,7 @@
         }
 
         private void requestCreateSessionOnHandler(Client2Record clientRecord,
-                MediaRoute2Info route, String controlCategory, long requestId) {
+                MediaRoute2Info route, String routeType, long requestId) {
 
             final MediaRoute2Provider provider = findProvider(route.getProviderId());
             if (provider == null) {
@@ -971,20 +988,20 @@
                 return;
             }
 
-            if (!route.getSupportedCategories().contains(controlCategory)) {
+            if (!route.getRouteTypes().contains(routeType)) {
                 Slog.w(TAG, "Ignoring session creation request since the given route=" + route
-                        + " doesn't support the given category=" + controlCategory);
+                        + " doesn't support the given type=" + routeType);
                 notifySessionCreationFailed(clientRecord, toClientRequestId(requestId));
                 return;
             }
 
             // TODO: Apply timeout for each request (How many seconds should we wait?)
             SessionCreationRequest request = new SessionCreationRequest(
-                    clientRecord, route, controlCategory, requestId);
+                    clientRecord, route, routeType, requestId);
             mSessionCreationRequests.add(request);
 
-            provider.requestCreateSession(clientRecord.mPackageName, route.getId(),
-                    controlCategory, requestId);
+            provider.requestCreateSession(clientRecord.mPackageName, route.getOriginalId(),
+                    routeType, requestId);
         }
 
         private void selectRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1000,7 +1017,7 @@
             if (provider == null) {
                 return;
             }
-            provider.selectRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route);
+            provider.selectRoute(getOriginalId(uniqueSessionId), route.getOriginalId());
         }
 
         private void deselectRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1016,7 +1033,7 @@
             if (provider == null) {
                 return;
             }
-            provider.deselectRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route);
+            provider.deselectRoute(getOriginalId(uniqueSessionId), route.getOriginalId());
         }
 
         private void transferToRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1032,7 +1049,8 @@
             if (provider == null) {
                 return;
             }
-            provider.transferToRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route);
+            provider.transferToRoute(getOriginalId(uniqueSessionId),
+                    route.getOriginalId());
         }
 
         private boolean checkArgumentsForSessionControl(@NonNull Client2Record clientRecord,
@@ -1063,9 +1081,9 @@
                 return false;
             }
 
-            final Integer sessionId = RouteSessionInfo.getSessionId(uniqueSessionId);
+            final String sessionId = getOriginalId(uniqueSessionId);
             if (sessionId == null) {
-                Slog.w(TAG, "Failed to get int session id from unique session id. "
+                Slog.w(TAG, "Failed to get original session id from unique session id. "
                         + "uniqueSessionId=" + uniqueSessionId);
                 return false;
             }
@@ -1088,14 +1106,14 @@
                 return;
             }
 
-            final String providerId = RouteSessionInfo.getProviderId(uniqueSessionId);
+            final String providerId = getProviderId(uniqueSessionId);
             if (providerId == null) {
                 Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. "
                         + "uniqueSessionId=" + uniqueSessionId);
                 return;
             }
 
-            final Integer sessionId = RouteSessionInfo.getSessionId(uniqueSessionId);
+            final String sessionId = getOriginalId(uniqueSessionId);
             if (sessionId == null) {
                 Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. "
                         + "uniqueSessionId=" + uniqueSessionId + " providerId=" + providerId);
@@ -1141,15 +1159,15 @@
             }
 
             String originalRouteId = matchingRequest.mRoute.getId();
-            String originalCategory = matchingRequest.mControlCategory;
+            String originalRouteType = matchingRequest.mRouteType;
             Client2Record client2Record = matchingRequest.mClientRecord;
 
             if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)
-                    || !TextUtils.equals(originalCategory,
-                        sessionInfo.getControlCategory())) {
+                    || !TextUtils.equals(originalRouteType,
+                        sessionInfo.getRouteType())) {
                 Slog.w(TAG, "Created session doesn't match the original request."
                         + " originalRouteId=" + originalRouteId
-                        + ", originalCategory=" + originalCategory + ", requestId=" + requestId
+                        + ", originalRouteType=" + originalRouteType + ", requestId=" + requestId
                         + ", sessionInfo=" + sessionInfo);
                 notifySessionCreationFailed(matchingRequest.mClientRecord,
                         toClientRequestId(requestId));
@@ -1159,41 +1177,34 @@
             // Succeeded
             notifySessionCreated(matchingRequest.mClientRecord,
                     sessionInfo, toClientRequestId(requestId));
-            mSessionToClientMap.put(sessionInfo.getUniqueSessionId(), client2Record);
+            mSessionToClientMap.put(sessionInfo.getId(), client2Record);
             // TODO: Tell managers for the session creation
         }
 
         private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
                 @NonNull RouteSessionInfo sessionInfo) {
-            RouteSessionInfo sessionInfoWithProviderId = new RouteSessionInfo.Builder(sessionInfo)
-                    .setProviderId(provider.getUniqueId())
-                    .build();
 
             Client2Record client2Record = mSessionToClientMap.get(
-                    sessionInfoWithProviderId.getUniqueSessionId());
+                    sessionInfo.getId());
             if (client2Record == null) {
-                Slog.w(TAG, "No matching client found for session=" + sessionInfoWithProviderId);
+                Slog.w(TAG, "No matching client found for session=" + sessionInfo);
                 // TODO: Tell managers for the session update
                 return;
             }
-            notifySessionInfoChanged(client2Record, sessionInfoWithProviderId);
+            notifySessionInfoChanged(client2Record, sessionInfo);
             // TODO: Tell managers for the session update
         }
 
         private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider,
                 @NonNull RouteSessionInfo sessionInfo) {
-            RouteSessionInfo sessionInfoWithProviderId = new RouteSessionInfo.Builder(sessionInfo)
-                    .setProviderId(provider.getUniqueId())
-                    .build();
 
-            Client2Record client2Record = mSessionToClientMap.get(
-                    sessionInfoWithProviderId.getUniqueSessionId());
+            Client2Record client2Record = mSessionToClientMap.get(sessionInfo.getId());
             if (client2Record == null) {
-                Slog.w(TAG, "No matching client found for session=" + sessionInfoWithProviderId);
+                Slog.w(TAG, "No matching client found for session=" + sessionInfo);
                 // TODO: Tell managers for the session release
                 return;
             }
-            notifySessionReleased(client2Record, sessionInfoWithProviderId);
+            notifySessionReleased(client2Record, sessionInfo);
             // TODO: Tell managers for the session release
         }
 
@@ -1239,21 +1250,21 @@
         private void sendControlRequest(MediaRoute2Info route, Intent request) {
             final MediaRoute2Provider provider = findProvider(route.getProviderId());
             if (provider != null) {
-                provider.sendControlRequest(route, request);
+                provider.sendControlRequest(route.getOriginalId(), request);
             }
         }
 
         private void requestSetVolume(MediaRoute2Info route, int volume) {
             final MediaRoute2Provider provider = findProvider(route.getProviderId());
             if (provider != null) {
-                provider.requestSetVolume(route, volume);
+                provider.requestSetVolume(route.getOriginalId(), volume);
             }
         }
 
         private void requestUpdateVolume(MediaRoute2Info route, int delta) {
             final MediaRoute2Provider provider = findProvider(route.getProviderId());
             if (provider != null) {
-                provider.requestUpdateVolume(route, delta);
+                provider.requestUpdateVolume(route.getOriginalId(), delta);
             }
         }
 
@@ -1401,8 +1412,8 @@
                 try {
                     manager.notifyRouteSelected(clientRecord.mPackageName,
                             clientRecord.mSelectedRoute);
-                    manager.notifyControlCategoriesChanged(clientRecord.mPackageName,
-                            clientRecord.mControlCategories);
+                    manager.notifyRouteTypesChanged(clientRecord.mPackageName,
+                            clientRecord.mDiscoveryRequest.getRouteTypes());
                 } catch (RemoteException ex) {
                     Slog.w(TAG, "Failed to update client usage. Manager probably died.", ex);
                 }
@@ -1421,15 +1432,15 @@
         final class SessionCreationRequest {
             public final Client2Record mClientRecord;
             public final MediaRoute2Info mRoute;
-            public final String mControlCategory;
+            public final String mRouteType;
             public final long mRequestId;
 
             SessionCreationRequest(@NonNull Client2Record clientRecord,
                     @NonNull MediaRoute2Info route,
-                    @NonNull String controlCategory, long requestId) {
+                    @NonNull String routeType, long requestId) {
                 mClientRecord = clientRecord;
                 mRoute = route;
-                mControlCategory = controlCategory;
+                mRouteType = routeType;
                 mRequestId = requestId;
             }
         }
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index d77f43b..c76555c 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -39,6 +39,7 @@
 import android.media.MediaRouterClientState;
 import android.media.RemoteDisplayState;
 import android.media.RemoteDisplayState.RemoteDisplayInfo;
+import android.media.RouteDiscoveryRequest;
 import android.media.RouteSessionInfo;
 import android.os.Binder;
 import android.os.Handler;
@@ -459,8 +460,8 @@
     // Binder call
     @Override
     public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
-            String controlCategory, int requestId) {
-        mService2.requestCreateSession(client, route, controlCategory, requestId);
+            String routeType, int requestId) {
+        mService2.requestCreateSession(client, route, routeType, requestId);
     }
 
     // Binder call
@@ -519,8 +520,8 @@
     }
     // Binder call
     @Override
-    public void setControlCategories(IMediaRouter2Client client, List<String> categories) {
-        mService2.setControlCategories(client, categories);
+    public void setDiscoveryRequest2(IMediaRouter2Client client, RouteDiscoveryRequest request) {
+        mService2.setDiscoveryRequest2(client, request);
     }
 
     // Binder call
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
new file mode 100644
index 0000000..f3241ee
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+package com.android.server.media;
+
+import android.media.MediaController2;
+import android.media.Session2CommandGroup;
+import android.media.Session2Token;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+
+/**
+ * Keeps the record of {@link Session2Token} helps to send command to the corresponding session.
+ */
+// TODO(jaewan): Do not call service method directly -- introduce listener instead.
+public class MediaSession2Record implements MediaSessionRecordImpl {
+    private static final String TAG = "MediaSession2Record";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private final Session2Token mSessionToken;
+    @GuardedBy("mLock")
+    private final HandlerExecutor mHandlerExecutor;
+    @GuardedBy("mLock")
+    private final MediaController2 mController;
+    @GuardedBy("mLock")
+    private final MediaSessionService mService;
+    @GuardedBy("mLock")
+    private boolean mIsConnected;
+
+    public MediaSession2Record(Session2Token sessionToken, MediaSessionService service,
+            Looper handlerLooper) {
+        mSessionToken = sessionToken;
+        mService = service;
+        mHandlerExecutor = new HandlerExecutor(new Handler(handlerLooper));
+        mController = new MediaController2.Builder(service.getContext(), sessionToken)
+                .setControllerCallback(mHandlerExecutor, new Controller2Callback())
+                .build();
+    }
+
+    @Override
+    public String getPackageName() {
+        return mSessionToken.getPackageName();
+    }
+
+    public Session2Token getSession2Token() {
+        return mSessionToken;
+    }
+
+    @Override
+    public int getUid() {
+        return mSessionToken.getUid();
+    }
+
+    @Override
+    public int getUserId() {
+        return UserHandle.getUserId(mSessionToken.getUid());
+    }
+
+    @Override
+    public boolean isSystemPriority() {
+        // System priority session is currently only allowed for telephony, and it's OK to stick to
+        // the media1 API at this moment.
+        return false;
+    }
+
+    @Override
+    public void adjustVolume(String packageName, String opPackageName, int pid, int uid,
+            boolean asSystemService, int direction, int flags, boolean useSuggested) {
+        // TODO(jaewan): Add API to adjust volume.
+    }
+
+    @Override
+    public boolean isActive() {
+        synchronized (mLock) {
+            return mIsConnected;
+        }
+    }
+
+    @Override
+    public boolean checkPlaybackActiveState(boolean expected) {
+        synchronized (mLock) {
+            return mIsConnected && mController.isPlaybackActive() == expected;
+        }
+    }
+
+    @Override
+    public boolean isPlaybackTypeLocal() {
+        // TODO(jaewan): Implement -- need API to know whether the playback is remote or local.
+        return true;
+    }
+
+    @Override
+    public void close() {
+        synchronized (mLock) {
+            // Call close regardless of the mIsAvailable. This may be called when it's not yet
+            // connected.
+            mController.close();
+        }
+    }
+
+    @Override
+    public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
+            KeyEvent ke, int sequenceId, ResultReceiver cb) {
+        // TODO(jaewan): Implement.
+        return false;
+    }
+
+    @Override
+    public void dump(PrintWriter pw, String prefix) {
+        pw.println(prefix + "token=" + mSessionToken);
+        pw.println(prefix + "controller=" + mController);
+
+        final String indent = prefix + "  ";
+        pw.println(indent + "playbackActive=" + mController.isPlaybackActive());
+    }
+
+    @Override
+    public String toString() {
+        // TODO(jaewan): Also add getId().
+        return getPackageName() + " (userId=" + getUserId() + ")";
+    }
+
+    private class Controller2Callback extends MediaController2.ControllerCallback {
+        @Override
+        public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) {
+            if (DEBUG) {
+                Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands);
+            }
+            synchronized (mLock) {
+                mIsConnected = true;
+            }
+            mService.onSessionActiveStateChanged(MediaSession2Record.this);
+        }
+
+        @Override
+        public void onDisconnected(MediaController2 controller) {
+            if (DEBUG) {
+                Log.d(TAG, "disconnected from " + mSessionToken);
+            }
+            synchronized (mLock) {
+                mIsConnected = false;
+            }
+            mService.onSessionDied(MediaSession2Record.this);
+        }
+
+        @Override
+        public void onPlaybackActiveChanged(MediaController2 controller, boolean playbackActive) {
+            if (DEBUG) {
+                Log.d(TAG, "playback active changed, " + mSessionToken + ", active="
+                        + playbackActive);
+            }
+            mService.onSessionPlaybackStateChanged(MediaSession2Record.this, playbackActive);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index aa24ed2..df115d0 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -56,13 +56,15 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
  * This is the system implementation of a Session. Apps will interact with the
  * MediaSession wrapper class instead.
  */
-public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable {
+// TODO(jaewan): Do not call service method directly -- introduce listener instead.
+public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionRecordImpl {
     private static final String TAG = "MediaSessionRecord";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -72,6 +74,24 @@
      */
     private static final int OPTIMISTIC_VOLUME_TIMEOUT = 1000;
 
+    /**
+     * These are states that usually indicate the user took an action and should
+     * bump priority regardless of the old state.
+     */
+    private static final List<Integer> ALWAYS_PRIORITY_STATES = Arrays.asList(
+            PlaybackState.STATE_FAST_FORWARDING,
+            PlaybackState.STATE_REWINDING,
+            PlaybackState.STATE_SKIPPING_TO_PREVIOUS,
+            PlaybackState.STATE_SKIPPING_TO_NEXT);
+    /**
+     * These are states that usually indicate the user took an action if they
+     * were entered from a non-priority state.
+     */
+    private static final List<Integer> TRANSITION_PRIORITY_STATES = Arrays.asList(
+            PlaybackState.STATE_BUFFERING,
+            PlaybackState.STATE_CONNECTING,
+            PlaybackState.STATE_PLAYING);
+
     private final MessageHandler mHandler;
 
     private final int mOwnerPid;
@@ -170,6 +190,7 @@
      *
      * @return Info that identifies this session.
      */
+    @Override
     public String getPackageName() {
         return mPackageName;
     }
@@ -188,6 +209,7 @@
      *
      * @return The UID for this session.
      */
+    @Override
     public int getUid() {
         return mOwnerUid;
     }
@@ -197,6 +219,7 @@
      *
      * @return The user id for this session.
      */
+    @Override
     public int getUserId() {
         return mUserId;
     }
@@ -207,6 +230,7 @@
      *
      * @return True if this is a system priority session, false otherwise
      */
+    @Override
     public boolean isSystemPriority() {
         return (mFlags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0;
     }
@@ -220,7 +244,6 @@
      * @param opPackageName The op package that made the original volume request.
      * @param pid The pid that made the original volume request.
      * @param uid The uid that made the original volume request.
-     * @param caller caller binder. can be {@code null} if it's from the volume key.
      * @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.
@@ -318,9 +341,13 @@
 
     /**
      * Check if this session has been set to active by the app.
+     * <p>
+     * It's not used to prioritize sessions for dispatching media keys since API 26, but still used
+     * to filter session list in MediaSessionManager#getActiveSessions().
      *
      * @return True if the session is active, false otherwise.
      */
+    @Override
     public boolean isActive() {
         return mIsActive && !mDestroyed;
     }
@@ -333,6 +360,7 @@
      * @param expected True if playback is expected to be active. false otherwise.
      * @return True if the session's playback matches with the expectation. false otherwise.
      */
+    @Override
     public boolean checkPlaybackActiveState(boolean expected) {
         if (mPlaybackState == null) {
             return false;
@@ -345,13 +373,14 @@
      *
      * @return {@code true} if the playback is local.
      */
-    public boolean isPlaybackLocal() {
+    @Override
+    public boolean isPlaybackTypeLocal() {
         return mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL;
     }
 
     @Override
     public void binderDied() {
-        mService.sessionDied(this);
+        mService.onSessionDied(this);
     }
 
     /**
@@ -383,7 +412,7 @@
      * @param sequenceId (optional) sequence id. Use this only when a wake lock is needed.
      * @param cb (optional) result receiver to receive callback. Use this only when a wake lock is
      *           needed.
-     * @return {@code true} if the attempt to send media button was successfuly.
+     * @return {@code true} if the attempt to send media button was successfully.
      *         {@code false} otherwise.
      */
     public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
@@ -392,6 +421,7 @@
                 cb);
     }
 
+    @Override
     public void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + mTag + " " + this);
 
@@ -712,7 +742,7 @@
         public void destroySession() throws RemoteException {
             final long token = Binder.clearCallingIdentity();
             try {
-                mService.destroySession(MediaSessionRecord.this);
+                mService.onSessionDied(MediaSessionRecord.this);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -734,7 +764,7 @@
             mIsActive = active;
             final long token = Binder.clearCallingIdentity();
             try {
-                mService.updateSession(MediaSessionRecord.this);
+                mService.onSessionActiveStateChanged(MediaSessionRecord.this);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -801,12 +831,16 @@
                     ? PlaybackState.STATE_NONE : mPlaybackState.getState();
             int newState = state == null
                     ? PlaybackState.STATE_NONE : state.getState();
+            boolean shouldUpdatePriority = ALWAYS_PRIORITY_STATES.contains(newState)
+                    || (!TRANSITION_PRIORITY_STATES.contains(oldState)
+                    && TRANSITION_PRIORITY_STATES.contains(newState));
             synchronized (mLock) {
                 mPlaybackState = state;
             }
             final long token = Binder.clearCallingIdentity();
             try {
-                mService.onSessionPlaystateChanged(MediaSessionRecord.this, oldState, newState);
+                mService.onSessionPlaybackStateChanged(
+                        MediaSessionRecord.this, shouldUpdatePriority);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
new file mode 100644
index 0000000..2cde89a7
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+package com.android.server.media;
+
+import android.media.AudioManager;
+import android.os.ResultReceiver;
+import android.view.KeyEvent;
+
+import java.io.PrintWriter;
+
+/**
+ * Common interfaces between {@link MediaSessionRecord} and {@link MediaSession2Record}.
+ */
+public interface MediaSessionRecordImpl extends AutoCloseable {
+
+    /**
+     * Get the info for this session.
+     *
+     * @return Info that identifies this session.
+     */
+    String getPackageName();
+
+    /**
+     * Get the UID this session was created for.
+     *
+     * @return The UID for this session.
+     */
+    int getUid();
+
+    /**
+     * Get the user id this session was created for.
+     *
+     * @return The user id for this session.
+     */
+    int getUserId();
+
+    /**
+     * Check if this session has system priorty and should receive media buttons
+     * before any other sessions.
+     *
+     * @return True if this is a system priority session, false otherwise
+     */
+    boolean isSystemPriority();
+
+    /**
+     * Send a volume adjustment to the session owner. Direction must be one of
+     * {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE},
+     * {@link AudioManager#ADJUST_SAME}.
+     *
+     * @param packageName The package that made the original volume request.
+     * @param opPackageName The op package that made the original volume request.
+     * @param pid The pid that made the original volume request.
+     * @param uid The uid that made the original volume request.
+     * @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 direction The direction to adjust volume in.
+     * @param flags Any of the flags from {@link AudioManager}.
+     * @param useSuggested True to use adjustSuggestedStreamVolume instead of
+     */
+    void adjustVolume(String packageName, String opPackageName, int pid, int uid,
+            boolean asSystemService, int direction, int flags, boolean useSuggested);
+
+    /**
+     * Check if this session has been set to active by the app. (i.e. ready to receive command and
+     * getters are available).
+     *
+     * @return True if the session is active, false otherwise.
+     */
+    // TODO(jaewan): Find better naming, or remove this from the MediaSessionRecordImpl.
+    boolean isActive();
+
+    /**
+     * Check if the session's playback active state matches with the expectation. This always return
+     * {@code false} if the playback state is unknown (e.g. {@code null}), where we cannot know the
+     * actual playback state associated with the session.
+     *
+     * @param expected True if playback is expected to be active. false otherwise.
+     * @return True if the session's playback matches with the expectation. false otherwise.
+     */
+    boolean checkPlaybackActiveState(boolean expected);
+
+    /**
+     * Check whether the playback type is local or remote.
+     * <p>
+     * <ul>
+     *   <li>Local: volume changes the stream volume because playback happens on this device.</li>
+     *   <li>Remote: volume is sent to the apps callback because playback happens on the remote
+     *     device and we cannot know how to control the volume of it.</li>
+     * </ul>
+     *
+     * @return {@code true} if the playback is local. {@code false} if the playback is remote.
+     */
+    boolean isPlaybackTypeLocal();
+
+    /**
+     * Sends media button.
+     *
+     * @param packageName caller package name
+     * @param pid caller pid
+     * @param uid caller uid
+     * @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.
+     * @param ke key events
+     * @param sequenceId (optional) sequence id. Use this only when a wake lock is needed.
+     * @param cb (optional) result receiver to receive callback. Use this only when a wake lock is
+     *           needed.
+     * @return {@code true} if the attempt to send media button was successfully.
+     *         {@code false} otherwise.
+     */
+    boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
+            KeyEvent ke, int sequenceId, ResultReceiver cb);
+
+    /**
+     * Dumps internal state
+     *
+     * @param pw print writer
+     * @param prefix prefix
+     */
+    void dump(PrintWriter pw, String prefix);
+
+    /**
+     * Override {@link AutoCloseable#close} to tell not to throw exception.
+     */
+    @Override
+    void close();
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 0f059db..f71fb58 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -40,10 +40,7 @@
 import android.media.AudioManagerInternal;
 import android.media.AudioPlaybackConfiguration;
 import android.media.AudioSystem;
-import android.media.IAudioService;
 import android.media.IRemoteVolumeController;
-import android.media.MediaController2;
-import android.media.Session2CommandGroup;
 import android.media.Session2Token;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.IOnMediaKeyEventDispatchedListener;
@@ -61,7 +58,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.PowerManager;
@@ -123,11 +119,6 @@
     @GuardedBy("mLock")
     private final ArrayList<SessionsListenerRecord> mSessionsListeners =
             new ArrayList<SessionsListenerRecord>();
-    // Map user id as index to list of Session2Tokens
-    // TODO: Keep session2 info in MediaSessionStack for prioritizing both session1 and session2 in
-    //       one place.
-    @GuardedBy("mLock")
-    private final SparseArray<List<Session2Token>> mSession2TokensPerUser = new SparseArray<>();
     @GuardedBy("mLock")
     private final List<Session2TokensListenerRecord> mSession2TokensListenerRecords =
             new ArrayList<>();
@@ -189,16 +180,11 @@
         updateUser();
     }
 
-    private IAudioService getAudioService() {
-        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
-        return IAudioService.Stub.asInterface(b);
-    }
-
     private boolean isGlobalPriorityActiveLocked() {
         return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
     }
 
-    void updateSession(MediaSessionRecord record) {
+    void onSessionActiveStateChanged(MediaSessionRecordImpl record) {
         synchronized (mLock) {
             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
             if (user == null) {
@@ -215,12 +201,14 @@
                     Log.w(TAG, "Unknown session updated. Ignoring.");
                     return;
                 }
-                user.mPriorityStack.onSessionStateChange(record);
+                user.mPriorityStack.onSessionActiveStateChanged(record);
             }
-            mHandler.postSessionsChanged(record.getUserId());
+
+            mHandler.postSessionsChanged(record);
         }
     }
 
+    // Currently only media1 can become global priority session.
     void setGlobalPrioritySession(MediaSessionRecord record) {
         synchronized (mLock) {
             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
@@ -266,11 +254,13 @@
     List<Session2Token> getSession2TokensLocked(int userId) {
         List<Session2Token> list = new ArrayList<>();
         if (userId == USER_ALL) {
-            for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
-                list.addAll(mSession2TokensPerUser.valueAt(i));
+            int size = mUserRecords.size();
+            for (int i = 0; i < size; i++) {
+                list.addAll(mUserRecords.valueAt(i).mPriorityStack.getSession2Tokens(userId));
             }
         } else {
-            list.addAll(mSession2TokensPerUser.get(userId));
+            FullUserRecord user = getFullUserRecordLocked(userId);
+            list.addAll(user.mPriorityStack.getSession2Tokens(userId));
         }
         return list;
     }
@@ -297,14 +287,15 @@
         }
     }
 
-    void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
+    void onSessionPlaybackStateChanged(MediaSessionRecordImpl record,
+            boolean shouldUpdatePriority) {
         synchronized (mLock) {
             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
             if (user == null || !user.mPriorityStack.contains(record)) {
                 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
                 return;
             }
-            user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
+            user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);
         }
     }
 
@@ -347,7 +338,6 @@
                     user.destroySessionsForUserLocked(userId);
                 }
             }
-            mSession2TokensPerUser.remove(userId);
             updateUser();
         }
     }
@@ -366,13 +356,7 @@
         }
     }
 
-    void sessionDied(MediaSessionRecord session) {
-        synchronized (mLock) {
-            destroySessionLocked(session);
-        }
-    }
-
-    void destroySession(MediaSessionRecord session) {
+    void onSessionDied(MediaSessionRecordImpl session) {
         synchronized (mLock) {
             destroySessionLocked(session);
         }
@@ -393,9 +377,6 @@
                             mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
                         }
                     }
-                    if (mSession2TokensPerUser.get(userInfo.id) == null) {
-                        mSession2TokensPerUser.put(userInfo.id, new ArrayList<>());
-                    }
                 }
             }
             // Ensure that the current full user exists.
@@ -405,9 +386,6 @@
                 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
                 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
                 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
-                if (mSession2TokensPerUser.get(currentFullUserId) == null) {
-                    mSession2TokensPerUser.put(currentFullUserId, new ArrayList<>());
-                }
             }
             mFullUserIds.put(currentFullUserId, currentFullUserId);
         }
@@ -444,7 +422,7 @@
      * 5. We need to unlink to death from the cb binder
      * 6. We need to tell the session to do any final cleanup (onDestroy)
      */
-    private void destroySessionLocked(MediaSessionRecord session) {
+    private void destroySessionLocked(MediaSessionRecordImpl session) {
         if (DEBUG) {
             Log.d(TAG, "Destroying " + session);
         }
@@ -461,7 +439,7 @@
         }
 
         session.close();
-        mHandler.postSessionsChanged(session.getUserId());
+        mHandler.postSessionsChanged(session);
     }
 
     private void enforcePackageName(String packageName, int uid) {
@@ -541,15 +519,6 @@
         return false;
     }
 
-    private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
-            String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo)
-            throws RemoteException {
-        synchronized (mLock) {
-            return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb,
-                    tag, sessionInfo);
-        }
-    }
-
     /*
      * When a session is created the following things need to happen.
      * 1. Its callback binder needs a link to death
@@ -557,29 +526,31 @@
      * 3. It needs to be added to the priority stack.
      * 4. It needs to be added to the relevant user record.
      */
-    private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
+    private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
             String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) {
-        FullUserRecord user = getFullUserRecordLocked(userId);
-        if (user == null) {
-            Log.w(TAG, "Request from invalid user: " +  userId + ", pkg=" + callerPackageName);
-            throw new RuntimeException("Session request from invalid user.");
-        }
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(userId);
+            if (user == null) {
+                Log.w(TAG, "Request from invalid user: " +  userId + ", pkg=" + callerPackageName);
+                throw new RuntimeException("Session request from invalid user.");
+            }
 
-        final MediaSessionRecord session;
-        try {
-            session = new MediaSessionRecord(callerPid, callerUid, userId,
-                    callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper());
-        } catch (RemoteException e) {
-            throw new RuntimeException("Media Session owner died prematurely.", e);
-        }
+            final MediaSessionRecord session;
+            try {
+                session = new MediaSessionRecord(callerPid, callerUid, userId,
+                        callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper());
+            } catch (RemoteException e) {
+                throw new RuntimeException("Media Session owner died prematurely.", e);
+            }
 
-        user.mPriorityStack.addSession(session);
-        mHandler.postSessionsChanged(userId);
+            user.mPriorityStack.addSession(session);
+            mHandler.postSessionsChanged(session);
 
-        if (DEBUG) {
-            Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
+            if (DEBUG) {
+                Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
+            }
+            return session;
         }
-        return session;
     }
 
     private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
@@ -600,16 +571,16 @@
         return -1;
     }
 
-    private void pushSessionsChanged(int userId) {
+    private void pushSession1Changed(int userId) {
         synchronized (mLock) {
             FullUserRecord user = getFullUserRecordLocked(userId);
             if (user == null) {
-                Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
+                Log.w(TAG, "pushSession1ChangedOnHandler failed. No user with id=" + userId);
                 return;
             }
             List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
             int size = records.size();
-            ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
+            ArrayList<MediaSession.Token> tokens = new ArrayList<>();
             for (int i = 0; i < size; i++) {
                 tokens.add(records.get(i).getSessionToken());
             }
@@ -629,6 +600,27 @@
         }
     }
 
+    void pushSession2Changed(int userId) {
+        synchronized (mLock) {
+            List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL);
+            List<Session2Token> session2Tokens = getSession2TokensLocked(userId);
+
+            for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) {
+                Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i);
+                try {
+                    if (listenerRecord.userId == USER_ALL) {
+                        listenerRecord.listener.onSession2TokensChanged(allSession2Tokens);
+                    } else if (listenerRecord.userId == userId) {
+                        listenerRecord.listener.onSession2TokensChanged(session2Tokens);
+                    }
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e);
+                    mSession2TokensListenerRecords.remove(i);
+                }
+            }
+        }
+    }
+
     private void pushRemoteVolumeUpdateLocked(int userId) {
         FullUserRecord user = getFullUserRecordLocked(userId);
         if (user == null) {
@@ -638,8 +630,13 @@
 
         synchronized (mLock) {
             int size = mRemoteVolumeControllers.beginBroadcast();
-            MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
-            MediaSession.Token token = record == null ? null : record.getSessionToken();
+            MediaSessionRecordImpl record = user.mPriorityStack.getDefaultRemoteSession(userId);
+            if (record instanceof MediaSession2Record) {
+                // TODO(jaewan): Implement
+                return;
+            }
+            MediaSession.Token token = record == null
+                    ? null : ((MediaSessionRecord) record).getSessionToken();
 
             for (int i = size - 1; i >= 0; i--) {
                 try {
@@ -653,34 +650,15 @@
         }
     }
 
-    void pushSession2TokensChangedLocked(int userId) {
-        List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL);
-        List<Session2Token> session2Tokens = getSession2TokensLocked(userId);
-
-        for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) {
-            Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i);
-            try {
-                if (listenerRecord.userId == USER_ALL) {
-                    listenerRecord.listener.onSession2TokensChanged(allSession2Tokens);
-                } else if (listenerRecord.userId == userId) {
-                    listenerRecord.listener.onSession2TokensChanged(session2Tokens);
-                }
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e);
-                mSession2TokensListenerRecords.remove(i);
-            }
-        }
-    }
-
     /**
      * Called when the media button receiver for the {@code record} is changed.
      *
      * @param record the media session whose media button receiver is updated.
      */
-    public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
+    public void onMediaButtonReceiverChanged(MediaSessionRecordImpl record) {
         synchronized (mLock) {
             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
-            MediaSessionRecord mediaButtonSession =
+            MediaSessionRecordImpl mediaButtonSession =
                     user.mPriorityStack.getMediaButtonSession();
             if (record == mediaButtonSession) {
                 user.rememberMediaButtonReceiverLocked(mediaButtonSession);
@@ -868,39 +846,34 @@
             pw.println(indent + "Restored MediaButtonReceiverComponentType: "
                     + mRestoredMediaButtonReceiverComponentType);
             mPriorityStack.dump(pw, indent);
-            pw.println(indent + "Session2Tokens:");
-            for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
-                List<Session2Token> list = mSession2TokensPerUser.valueAt(i);
-                if (list == null || list.size() == 0) {
-                    continue;
-                }
-                for (Session2Token token : list) {
-                    pw.println(indent + "  " + token);
-                }
-            }
         }
 
         @Override
-        public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
-                MediaSessionRecord newMediaButtonSession) {
+        public void onMediaButtonSessionChanged(MediaSessionRecordImpl oldMediaButtonSession,
+                MediaSessionRecordImpl newMediaButtonSession) {
             if (DEBUG_KEY_EVENT) {
                 Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
             }
             synchronized (mLock) {
                 if (oldMediaButtonSession != null) {
-                    mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
+                    mHandler.postSessionsChanged(oldMediaButtonSession);
                 }
                 if (newMediaButtonSession != null) {
                     rememberMediaButtonReceiverLocked(newMediaButtonSession);
-                    mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
+                    mHandler.postSessionsChanged(newMediaButtonSession);
                 }
                 pushAddressedPlayerChangedLocked();
             }
         }
 
         // Remember media button receiver and keep it in the persistent storage.
-        public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
-            PendingIntent receiver = record.getMediaButtonReceiver();
+        public void rememberMediaButtonReceiverLocked(MediaSessionRecordImpl record) {
+            if (record instanceof MediaSession2Record) {
+                // TODO(jaewan): Implement
+                return;
+            }
+            MediaSessionRecord sessionRecord = (MediaSessionRecord) record;
+            PendingIntent receiver = sessionRecord.getMediaButtonReceiver();
             mLastMediaButtonReceiver = receiver;
             mRestoredMediaButtonReceiver = null;
             mRestoredMediaButtonReceiverComponentType = COMPONENT_TYPE_INVALID;
@@ -925,10 +898,15 @@
         private void pushAddressedPlayerChangedLocked(
                 IOnMediaKeyEventSessionChangedListener callback) {
             try {
-                MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
+                MediaSessionRecordImpl mediaButtonSession = getMediaButtonSessionLocked();
                 if (mediaButtonSession != null) {
-                    callback.onMediaKeyEventSessionChanged(mediaButtonSession.getPackageName(),
-                            mediaButtonSession.getSessionToken());
+                    if (mediaButtonSession instanceof MediaSessionRecord) {
+                        MediaSessionRecord session1 = (MediaSessionRecord) mediaButtonSession;
+                        callback.onMediaKeyEventSessionChanged(session1.getPackageName(),
+                                session1.getSessionToken());
+                    } else {
+                        // TODO(jaewan): Implement
+                    }
                 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
                     callback.onMediaKeyEventSessionChanged(
                             mCurrentFullUserRecord.mLastMediaButtonReceiver
@@ -951,7 +929,7 @@
             }
         }
 
-        private MediaSessionRecord getMediaButtonSessionLocked() {
+        private MediaSessionRecordImpl getMediaButtonSessionLocked() {
             return isGlobalPriorityActiveLocked()
                     ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
         }
@@ -1132,14 +1110,13 @@
                     throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
                             + " but actually=" + sessionToken.getUid());
                 }
-                Controller2Callback callback = new Controller2Callback(sessionToken);
-                // Note: It's safe not to keep controller here because it wouldn't be GC'ed until
-                //       it's closed.
-                // TODO: Keep controller as well for better readability
-                //       because the GC behavior isn't straightforward.
-                MediaController2 controller = new MediaController2.Builder(mContext, sessionToken)
-                        .setControllerCallback(new HandlerExecutor(mHandler), callback)
-                        .build();
+                MediaSession2Record record = new MediaSession2Record(
+                        sessionToken, MediaSessionService.this, mHandler.getLooper());
+                synchronized (mLock) {
+                    FullUserRecord user = getFullUserRecordLocked(record.getUserId());
+                    user.mPriorityStack.addSession(record);
+                }
+                // Do not immediately notify changes -- do so when framework can dispatch command
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1180,7 +1157,8 @@
                         null /* optional packageName */);
                 List<Session2Token> result;
                 synchronized (mLock) {
-                    result = getSession2TokensLocked(resolvedUserId);
+                    FullUserRecord user = getFullUserRecordLocked(userId);
+                    result = user.mPriorityStack.getSession2Tokens(resolvedUserId);
                 }
                 return new ParceledListSlice(result);
             } finally {
@@ -2018,7 +1996,7 @@
 
         private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid,
                 int uid, boolean asSystemService, int suggestedStream, int direction, int flags) {
-            MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
+            MediaSessionRecordImpl session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
                     : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
 
             boolean preferSuggestedStream = false;
@@ -2109,7 +2087,13 @@
 
         private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
                 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
-            MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
+            if (mCurrentFullUserRecord.getMediaButtonSessionLocked()
+                    instanceof MediaSession2Record) {
+                // TODO(jaewan): Implement
+                return;
+            }
+            MediaSessionRecord session =
+                    (MediaSessionRecord) mCurrentFullUserRecord.getMediaButtonSessionLocked();
             if (session != null) {
                 if (DEBUG_KEY_EVENT) {
                     Log.d(TAG, "Sending " + keyEvent + " to " + session);
@@ -2389,15 +2373,19 @@
     }
 
     final class MessageHandler extends Handler {
-        private static final int MSG_SESSIONS_CHANGED = 1;
-        private static final int MSG_VOLUME_INITIAL_DOWN = 2;
+        private static final int MSG_SESSIONS_1_CHANGED = 1;
+        private static final int MSG_SESSIONS_2_CHANGED = 2;
+        private static final int MSG_VOLUME_INITIAL_DOWN = 3;
         private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
 
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_SESSIONS_CHANGED:
-                    pushSessionsChanged((int) msg.obj);
+                case MSG_SESSIONS_1_CHANGED:
+                    pushSession1Changed((int) msg.obj);
+                    break;
+                case MSG_SESSIONS_2_CHANGED:
+                    pushSession2Changed((int) msg.obj);
                     break;
                 case MSG_VOLUME_INITIAL_DOWN:
                     synchronized (mLock) {
@@ -2412,41 +2400,19 @@
             }
         }
 
-        public void postSessionsChanged(int userId) {
+        public void postSessionsChanged(MediaSessionRecordImpl record) {
             // Use object instead of the arguments when posting message to remove pending requests.
-            Integer userIdInteger = mIntegerCache.get(userId);
+            Integer userIdInteger = mIntegerCache.get(record.getUserId());
             if (userIdInteger == null) {
-                userIdInteger = Integer.valueOf(userId);
-                mIntegerCache.put(userId, userIdInteger);
+                userIdInteger = Integer.valueOf(record.getUserId());
+                mIntegerCache.put(record.getUserId(), userIdInteger);
             }
-            removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
-            obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
+
+            int msg = (record instanceof MediaSessionRecord)
+                    ? MSG_SESSIONS_1_CHANGED : MSG_SESSIONS_2_CHANGED;
+            removeMessages(msg, userIdInteger);
+            obtainMessage(msg, userIdInteger).sendToTarget();
         }
     }
 
-    private class Controller2Callback extends MediaController2.ControllerCallback {
-        private final Session2Token mToken;
-
-        Controller2Callback(Session2Token token) {
-            mToken = token;
-        }
-
-        @Override
-        public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) {
-            synchronized (mLock) {
-                int userId = UserHandle.getUserId(mToken.getUid());
-                mSession2TokensPerUser.get(userId).add(mToken);
-                pushSession2TokensChangedLocked(userId);
-            }
-        }
-
-        @Override
-        public void onDisconnected(MediaController2 controller) {
-            synchronized (mLock) {
-                int userId = UserHandle.getUserId(mToken.getUid());
-                mSession2TokensPerUser.get(userId).remove(mToken);
-                pushSession2TokensChangedLocked(userId);
-            }
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 732563f..7bb7cf4 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -16,8 +16,8 @@
 
 package com.android.server.media;
 
+import android.media.Session2Token;
 import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
 import android.os.Debug;
 import android.os.UserHandle;
 import android.util.IntArray;
@@ -45,51 +45,30 @@
         /**
          * Called when the media button session is changed.
          */
-        void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
-                MediaSessionRecord newMediaButtonSession);
+        void onMediaButtonSessionChanged(MediaSessionRecordImpl oldMediaButtonSession,
+                MediaSessionRecordImpl newMediaButtonSession);
     }
 
     /**
-     * These are states that usually indicate the user took an action and should
-     * bump priority regardless of the old state.
+     * Sorted list of the media sessions
      */
-    private static final int[] ALWAYS_PRIORITY_STATES = {
-            PlaybackState.STATE_FAST_FORWARDING,
-            PlaybackState.STATE_REWINDING,
-            PlaybackState.STATE_SKIPPING_TO_PREVIOUS,
-            PlaybackState.STATE_SKIPPING_TO_NEXT };
-    /**
-     * These are states that usually indicate the user took an action if they
-     * were entered from a non-priority state.
-     */
-    private static final int[] TRANSITION_PRIORITY_STATES = {
-            PlaybackState.STATE_BUFFERING,
-            PlaybackState.STATE_CONNECTING,
-            PlaybackState.STATE_PLAYING };
-
-    /**
-     * Sorted list of the media sessions.
-     * The session of which PlaybackState is changed to ALWAYS_PRIORITY_STATES or
-     * TRANSITION_PRIORITY_STATES comes first.
-     * @see #shouldUpdatePriority
-     */
-    private final List<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
+    private final List<MediaSessionRecordImpl> mSessions = new ArrayList<>();
 
     private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
     private final OnMediaButtonSessionChangedListener mOnMediaButtonSessionChangedListener;
 
     /**
      * The media button session which receives media key events.
-     * It could be null if the previous media buttion session is released.
+     * It could be null if the previous media button session is released.
      */
-    private MediaSessionRecord mMediaButtonSession;
+    private MediaSessionRecordImpl mMediaButtonSession;
 
-    private MediaSessionRecord mCachedVolumeDefault;
+    private MediaSessionRecordImpl mCachedVolumeDefault;
 
     /**
      * Cache the result of the {@link #getActiveSessions} per user.
      */
-    private final SparseArray<ArrayList<MediaSessionRecord>> mCachedActiveLists =
+    private final SparseArray<List<MediaSessionRecord>> mCachedActiveLists =
             new SparseArray<>();
 
     MediaSessionStack(AudioPlayerStateMonitor monitor, OnMediaButtonSessionChangedListener listener) {
@@ -102,7 +81,7 @@
      *
      * @param record The record to add.
      */
-    public void addSession(MediaSessionRecord record) {
+    public void addSession(MediaSessionRecordImpl record) {
         mSessions.add(record);
         clearCache(record.getUserId());
 
@@ -117,7 +96,7 @@
      *
      * @param record The record to remove.
      */
-    public void removeSession(MediaSessionRecord record) {
+    public void removeSession(MediaSessionRecordImpl record) {
         mSessions.remove(record);
         if (mMediaButtonSession == record) {
             // When the media button session is removed, nullify the media button session and do not
@@ -131,7 +110,7 @@
     /**
      * Return if the record exists in the priority tracker.
      */
-    public boolean contains(MediaSessionRecord record) {
+    public boolean contains(MediaSessionRecordImpl record) {
         return mSessions.contains(record);
     }
 
@@ -142,9 +121,12 @@
      * @return the MediaSessionRecord. Can be {@code null} if the session is gone meanwhile.
      */
     public MediaSessionRecord getMediaSessionRecord(MediaSession.Token sessionToken) {
-        for (MediaSessionRecord record : mSessions) {
-            if (Objects.equals(record.getSessionToken(), sessionToken)) {
-                return record;
+        for (MediaSessionRecordImpl record : mSessions) {
+            if (record instanceof MediaSessionRecord) {
+                MediaSessionRecord session1 = (MediaSessionRecord) record;
+                if (Objects.equals(session1.getSessionToken(), sessionToken)) {
+                    return session1;
+                }
             }
         }
         return null;
@@ -154,15 +136,15 @@
      * Notify the priority tracker that a session's playback state changed.
      *
      * @param record The record that changed.
-     * @param oldState Its old playback state.
-     * @param newState Its new playback state.
+     * @param shouldUpdatePriority {@code true} if the record needs to prioritized
      */
-    public void onPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
-        if (shouldUpdatePriority(oldState, newState)) {
+    public void onPlaybackStateChanged(
+            MediaSessionRecordImpl record, boolean shouldUpdatePriority) {
+        if (shouldUpdatePriority) {
             mSessions.remove(record);
             mSessions.add(0, record);
             clearCache(record.getUserId());
-        } else if (!MediaSession.isActiveState(newState)) {
+        } else if (record.checkPlaybackActiveState(false)) {
             // Just clear the volume cache when a state goes inactive
             mCachedVolumeDefault = null;
         }
@@ -172,7 +154,7 @@
         // In that case, we pick the media session whose PlaybackState matches
         // the audio playback configuration.
         if (mMediaButtonSession != null && mMediaButtonSession.getUid() == record.getUid()) {
-            MediaSessionRecord newMediaButtonSession =
+            MediaSessionRecordImpl newMediaButtonSession =
                     findMediaButtonSession(mMediaButtonSession.getUid());
             if (newMediaButtonSession != mMediaButtonSession) {
                 updateMediaButtonSession(newMediaButtonSession);
@@ -185,7 +167,7 @@
      *
      * @param record The record that changed.
      */
-    public void onSessionStateChange(MediaSessionRecord record) {
+    public void onSessionActiveStateChanged(MediaSessionRecordImpl record) {
         // For now just clear the cache. Eventually we'll selectively clear
         // depending on what changed.
         clearCache(record.getUserId());
@@ -203,7 +185,7 @@
         }
         IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
         for (int i = 0; i < audioPlaybackUids.size(); i++) {
-            MediaSessionRecord mediaButtonSession =
+            MediaSessionRecordImpl mediaButtonSession =
                     findMediaButtonSession(audioPlaybackUids.get(i));
             if (mediaButtonSession != null) {
                 // Found the media button session.
@@ -225,9 +207,9 @@
      * @return The media button session. Returns {@code null} if the app doesn't have a media
      *   session.
      */
-    private MediaSessionRecord findMediaButtonSession(int uid) {
-        MediaSessionRecord mediaButtonSession = null;
-        for (MediaSessionRecord session : mSessions) {
+    private MediaSessionRecordImpl findMediaButtonSession(int uid) {
+        MediaSessionRecordImpl mediaButtonSession = null;
+        for (MediaSessionRecordImpl session : mSessions) {
             if (uid == session.getUid()) {
                 if (session.checkPlaybackActiveState(
                         mAudioPlayerStateMonitor.isPlaybackActive(session.getUid()))) {
@@ -253,8 +235,8 @@
      *    for all users in this {@link MediaSessionStack}.
      * @return All the active sessions in priority order.
      */
-    public ArrayList<MediaSessionRecord> getActiveSessions(int userId) {
-        ArrayList<MediaSessionRecord> cachedActiveList = mCachedActiveLists.get(userId);
+    public List<MediaSessionRecord> getActiveSessions(int userId) {
+        List<MediaSessionRecord> cachedActiveList = mCachedActiveLists.get(userId);
         if (cachedActiveList == null) {
             cachedActiveList = getPriorityList(true, userId);
             mCachedActiveLists.put(userId, cachedActiveList);
@@ -263,26 +245,46 @@
     }
 
     /**
+     * Gets the session2 tokens.
+     *
+     * @param userId The user to check. It can be {@link UserHandle#USER_ALL} to get all session2
+     *    tokens for all users in this {@link MediaSessionStack}.
+     * @return All session2 tokens.
+     */
+    public List<Session2Token> getSession2Tokens(int userId) {
+        ArrayList<Session2Token> session2Records = new ArrayList<>();
+        for (MediaSessionRecordImpl record : mSessions) {
+            if ((userId == UserHandle.USER_ALL || record.getUserId() == userId)
+                    && record.isActive()
+                    && record instanceof MediaSession2Record) {
+                MediaSession2Record session2 = (MediaSession2Record) record;
+                session2Records.add(session2.getSession2Token());
+            }
+        }
+        return session2Records;
+    }
+
+    /**
      * Get the media button session which receives the media button events.
      *
      * @return The media button session or null.
      */
-    public MediaSessionRecord getMediaButtonSession() {
+    public MediaSessionRecordImpl getMediaButtonSession() {
         return mMediaButtonSession;
     }
 
-    private void updateMediaButtonSession(MediaSessionRecord newMediaButtonSession) {
-        MediaSessionRecord oldMediaButtonSession = mMediaButtonSession;
+    private void updateMediaButtonSession(MediaSessionRecordImpl newMediaButtonSession) {
+        MediaSessionRecordImpl oldMediaButtonSession = mMediaButtonSession;
         mMediaButtonSession = newMediaButtonSession;
         mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged(
                 oldMediaButtonSession, newMediaButtonSession);
     }
 
-    public MediaSessionRecord getDefaultVolumeSession() {
+    public MediaSessionRecordImpl getDefaultVolumeSession() {
         if (mCachedVolumeDefault != null) {
             return mCachedVolumeDefault;
         }
-        ArrayList<MediaSessionRecord> records = getPriorityList(true, UserHandle.USER_ALL);
+        List<MediaSessionRecord> records = getPriorityList(true, UserHandle.USER_ALL);
         int size = records.size();
         for (int i = 0; i < size; i++) {
             MediaSessionRecord record = records.get(i);
@@ -294,13 +296,13 @@
         return null;
     }
 
-    public MediaSessionRecord getDefaultRemoteSession(int userId) {
-        ArrayList<MediaSessionRecord> records = getPriorityList(true, userId);
+    public MediaSessionRecordImpl getDefaultRemoteSession(int userId) {
+        List<MediaSessionRecord> records = getPriorityList(true, userId);
 
         int size = records.size();
         for (int i = 0; i < size; i++) {
             MediaSessionRecord record = records.get(i);
-            if (!record.isPlaybackLocal()) {
+            if (!record.isPlaybackTypeLocal()) {
                 return record;
             }
         }
@@ -308,16 +310,11 @@
     }
 
     public void dump(PrintWriter pw, String prefix) {
-        ArrayList<MediaSessionRecord> sortedSessions = getPriorityList(false,
-                UserHandle.USER_ALL);
-        int count = sortedSessions.size();
         pw.println(prefix + "Media button session is " + mMediaButtonSession);
-        pw.println(prefix + "Sessions Stack - have " + count + " sessions:");
+        pw.println(prefix + "Sessions Stack - have " + mSessions.size() + " sessions:");
         String indent = prefix + "  ";
-        for (int i = 0; i < count; i++) {
-            MediaSessionRecord record = sortedSessions.get(i);
+        for (MediaSessionRecordImpl record : mSessions) {
             record.dump(pw, indent);
-            pw.println();
         }
     }
 
@@ -335,17 +332,19 @@
      *            will return sessions for all users.
      * @return The priority sorted list of sessions.
      */
-    public ArrayList<MediaSessionRecord> getPriorityList(boolean activeOnly, int userId) {
-        ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
+    public List<MediaSessionRecord> getPriorityList(boolean activeOnly, int userId) {
+        List<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
         int lastPlaybackActiveIndex = 0;
         int lastActiveIndex = 0;
 
-        int size = mSessions.size();
-        for (int i = 0; i < size; i++) {
-            final MediaSessionRecord session = mSessions.get(i);
+        for (MediaSessionRecordImpl record : mSessions) {
+            if (!(record instanceof MediaSessionRecord)) {
+                continue;
+            }
+            final MediaSessionRecord session = (MediaSessionRecord) record;
 
-            if (userId != UserHandle.USER_ALL && userId != session.getUserId()) {
-                // Filter out sessions for the wrong user
+            if ((userId != UserHandle.USER_ALL && userId != session.getUserId())) {
+                // Filter out sessions for the wrong user or session2.
                 continue;
             }
 
@@ -369,26 +368,6 @@
         return result;
     }
 
-    private boolean shouldUpdatePriority(int oldState, int newState) {
-        if (containsState(newState, ALWAYS_PRIORITY_STATES)) {
-            return true;
-        }
-        if (!containsState(oldState, TRANSITION_PRIORITY_STATES)
-                && containsState(newState, TRANSITION_PRIORITY_STATES)) {
-            return true;
-        }
-        return false;
-    }
-
-    private boolean containsState(int state, int[] states) {
-        for (int i = 0; i < states.length; i++) {
-            if (states[i] == state) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private void clearCache(int userId) {
         mCachedVolumeDefault = null;
         mCachedActiveLists.remove(userId);
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 8fdfcbf..0ea4e63 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -48,8 +48,8 @@
     static final String BLUETOOTH_ROUTE_ID = "BLUETOOTH_ROUTE";
 
     // TODO: Move these to a proper place
-    public static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
-    public static final String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
+    public static final String TYPE_LIVE_AUDIO = "android.media.intent.route.TYPE_LIVE_AUDIO";
+    public static final String TYPE_LIVE_VIDEO = "android.media.intent.route.TYPE_LIVE_VIDEO";
 
     private final AudioManager mAudioManager;
     private final IAudioService mAudioService;
@@ -91,44 +91,44 @@
     }
 
     @Override
-    public void requestCreateSession(String packageName, String routeId, String controlCategory,
+    public void requestCreateSession(String packageName, String routeId, String routeType,
             long requestId) {
         // Do nothing
     }
 
     @Override
-    public void releaseSession(int sessionId) {
+    public void releaseSession(String sessionId) {
         // Do nothing
     }
 
     @Override
-    public void selectRoute(int sessionId, MediaRoute2Info route) {
+    public void selectRoute(String sessionId, String routeId) {
         //TODO: implement method
     }
 
     @Override
-    public void deselectRoute(int sessionId, MediaRoute2Info route) {
+    public void deselectRoute(String sessionId, String routeId) {
         //TODO: implement method
     }
 
     @Override
-    public void transferToRoute(int sessionId, MediaRoute2Info route) {
+    public void transferToRoute(String sessionId, String routeId) {
         //TODO: implement method
     }
 
     //TODO: implement method
     @Override
-    public void sendControlRequest(@NonNull MediaRoute2Info route, @NonNull Intent request) {
+    public void sendControlRequest(@NonNull String routeId, @NonNull Intent request) {
     }
 
     //TODO: implement method
     @Override
-    public void requestSetVolume(MediaRoute2Info route, int volume) {
+    public void requestSetVolume(String routeId, int volume) {
     }
 
     //TODO: implement method
     @Override
-    public void requestUpdateVolume(MediaRoute2Info route, int delta) {
+    public void requestUpdateVolume(String routeId, int delta) {
     }
 
     void initializeRoutes() {
@@ -141,8 +141,8 @@
                         : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
                 .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
                 .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
-                .addSupportedCategory(CATEGORY_LIVE_AUDIO)
-                .addSupportedCategory(CATEGORY_LIVE_VIDEO)
+                .addRouteType(TYPE_LIVE_AUDIO)
+                .addRouteType(TYPE_LIVE_VIDEO)
                 .build();
 
         AudioRoutesInfo newAudioRoutes = null;
@@ -181,8 +181,8 @@
                         : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
                 .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
                 .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
-                .addSupportedCategory(CATEGORY_LIVE_AUDIO)
-                .addSupportedCategory(CATEGORY_LIVE_VIDEO)
+                .addRouteType(TYPE_LIVE_AUDIO)
+                .addRouteType(TYPE_LIVE_VIDEO)
                 .build();
 
         if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
@@ -193,7 +193,7 @@
                         mCurAudioRoutesInfo.bluetoothName)
                         .setDescription(mContext.getResources().getText(
                                 R.string.bluetooth_a2dp_audio_route_name).toString())
-                        .addSupportedCategory(CATEGORY_LIVE_AUDIO)
+                        .addRouteType(TYPE_LIVE_AUDIO)
                         .build();
             } else {
                 mBluetoothA2dpRoute = null;
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index 3ca1803..22b01be 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -229,7 +229,7 @@
                     entry.txPackets += reader.nextLong();
                 }
 
-                stats.addValues(entry);
+                stats.addEntry(entry);
                 reader.finishLine();
             }
         } catch (NullPointerException|NumberFormatException e) {
@@ -279,7 +279,7 @@
                 entry.txBytes = reader.nextLong();
                 entry.txPackets = reader.nextLong();
 
-                stats.addValues(entry);
+                stats.addEntry(entry);
                 reader.finishLine();
             }
         } catch (NullPointerException|NumberFormatException e) {
@@ -439,7 +439,7 @@
                 if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
                         && (limitUid == UID_ALL || limitUid == entry.uid)
                         && (limitTag == TAG_ALL || limitTag == entry.tag)) {
-                    stats.addValues(entry);
+                    stats.addEntry(entry);
                 }
 
                 reader.finishLine();
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index ec8a8e7..7a6f297 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -27,6 +27,7 @@
 import static android.net.NetworkStack.checkNetworkStackPermission;
 import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
 import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.IFACE_VT;
 import static android.net.NetworkStats.INTERFACES_ALL;
 import static android.net.NetworkStats.METERED_ALL;
 import static android.net.NetworkStats.ROAMING_ALL;
@@ -211,7 +212,8 @@
     /**
      * Virtual network interface for video telephony. This is for VT data usage counting purpose.
      */
-    public static final String VT_INTERFACE = "vt_data0";
+    // TODO: Remove this after no one is using it.
+    public static final String VT_INTERFACE = NetworkStats.IFACE_VT;
 
     /**
      * Settings that can be changed externally.
@@ -712,7 +714,7 @@
         final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
 
         final NetworkStats stats = new NetworkStats(end - start, 1);
-        stats.addValues(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE,
+        stats.addEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE,
                 METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, entry.rxBytes, entry.rxPackets,
                 entry.txBytes, entry.txPackets, entry.operations));
         return stats;
@@ -1179,8 +1181,8 @@
                                 ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
                                 ident.getRoaming(), true /* metered */,
                                 true /* onDefaultNetwork */);
-                        findOrCreateNetworkIdentitySet(mActiveIfaces, VT_INTERFACE).add(vtIdent);
-                        findOrCreateNetworkIdentitySet(mActiveUidIfaces, VT_INTERFACE).add(vtIdent);
+                        findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
+                        findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
                     }
 
                     if (isMobile) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 426cd01..4dcdf7e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -140,6 +140,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
@@ -209,6 +210,8 @@
     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
     private static final int[] EMPTY_CHILD_SESSION_ARRAY = {};
 
+    private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
+
     // TODO: enforce INSTALL_ALLOW_TEST
     // TODO: enforce INSTALL_ALLOW_DOWNGRADE
 
@@ -555,6 +558,12 @@
                                 params.dataLoaderParams);
             }
         }
+
+        if (isStreamingInstallation()
+                && this.params.dataLoaderParams.getComponentName().getPackageName()
+                == SYSTEM_DATA_LOADER_PACKAGE) {
+            assertShellOrSystemCalling("System data loaders");
+        }
     }
 
     public SessionInfo generateInfo() {
@@ -770,6 +779,17 @@
         }
     }
 
+    private void assertShellOrSystemCalling(String operation) {
+        switch (Binder.getCallingUid()) {
+            case android.os.Process.SHELL_UID:
+            case android.os.Process.ROOT_UID:
+            case android.os.Process.SYSTEM_UID:
+                break;
+            default:
+                throw new SecurityException(operation + " only supported from shell or system");
+        }
+    }
+
     private void assertCanWrite(boolean reverseMode) {
         if (isDataLoaderInstallation()) {
             throw new IllegalStateException(
@@ -780,15 +800,7 @@
             assertPreparedAndNotSealedLocked("assertCanWrite");
         }
         if (reverseMode) {
-            switch (Binder.getCallingUid()) {
-                case android.os.Process.SHELL_UID:
-                case android.os.Process.ROOT_UID:
-                case android.os.Process.SYSTEM_UID:
-                    break;
-                default:
-                    throw new SecurityException(
-                            "Reverse mode only supported from shell or system");
-            }
+            assertShellOrSystemCalling("Reverse mode");
         }
     }
 
@@ -1025,13 +1037,24 @@
         mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
     }
 
-    private class FileSystemConnector extends IPackageInstallerSessionFileSystemConnector.Stub {
+    private final class FileSystemConnector extends
+            IPackageInstallerSessionFileSystemConnector.Stub {
+        final Set<String> mAddedFiles;
+
+        FileSystemConnector(List<InstallationFile> addedFiles) {
+            mAddedFiles = addedFiles.stream().map(file -> file.getName()).collect(
+                    Collectors.toSet());
+        }
+
         @Override
         public void writeData(String name, long offsetBytes, long lengthBytes,
                 ParcelFileDescriptor incomingFd) {
             if (incomingFd == null) {
                 throw new IllegalArgumentException("incomingFd can't be null");
             }
+            if (!mAddedFiles.contains(name)) {
+                throw new SecurityException("File name is not in the list of added files.");
+            }
             try {
                 doWriteInternal(name, offsetBytes, lengthBytes, incomingFd);
             } catch (IOException e) {
@@ -2467,8 +2490,6 @@
             return;
         }
 
-        FileSystemConnector connector = new FileSystemConnector();
-
         List<InstallationFile> addedFiles = mFiles.stream().filter(
                 file -> sAddedFilter.accept(new File(file.name))).map(
                     file -> new InstallationFile(
@@ -2480,6 +2501,8 @@
                             0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect(
                 Collectors.toList());
 
+        final FileSystemConnector connector = new FileSystemConnector(addedFiles);
+
         DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class);
         if (dataLoaderManager == null) {
             throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
@@ -2515,11 +2538,11 @@
             }
         };
 
-        final DataLoaderParams params = this.params.dataLoaderParams;
-
         final FileSystemControlParcel control = new FileSystemControlParcel();
         control.callback = connector;
 
+        final DataLoaderParams params = this.params.dataLoaderParams;
+
         Bundle dataLoaderParams = new Bundle();
         dataLoaderParams.putParcelable("componentName", params.getComponentName());
         dataLoaderParams.putParcelable("control", control);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 10e2780..5adab37 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -34,7 +34,6 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.DataLoaderParams;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageInstaller;
@@ -137,10 +136,6 @@
     private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/";
     private static final int DEFAULT_WAIT_MS = 60 * 1000;
 
-    private static final String DATA_LOADER_PACKAGE = "android";
-    private static final String DATA_LOADER_CLASS =
-            "com.android.server.pm.PackageManagerShellCommandDataLoader";
-
     final IPackageManager mInterface;
     final IPermissionManager mPermissionManager;
     final private WeakHashMap<String, Resources> mResourceCache =
@@ -164,7 +159,7 @@
 
         final PrintWriter pw = getOutPrintWriter();
         try {
-            switch(cmd) {
+            switch (cmd) {
                 case "path":
                     return runPath();
                 case "dump":
@@ -1163,9 +1158,8 @@
     private int runStreamingInstall() throws RemoteException {
         final InstallParams params = makeInstallParams();
         if (params.sessionParams.dataLoaderParams == null) {
-            final DataLoaderParams dataLoaderParams = DataLoaderParams.forStreaming(
-                    new ComponentName(DATA_LOADER_PACKAGE, DATA_LOADER_CLASS), "");
-            params.sessionParams.setDataLoaderParams(dataLoaderParams);
+            params.sessionParams.setDataLoaderParams(
+                    PackageManagerShellCommandDataLoader.getDataLoaderParams(this));
         }
         return doRunInstall(params);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index 1ee9ab8..a814cb8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -17,18 +17,22 @@
 package com.android.server.pm;
 
 import android.annotation.NonNull;
+import android.content.ComponentName;
 import android.content.pm.DataLoaderParams;
 import android.content.pm.InstallationFile;
 import android.os.ParcelFileDescriptor;
+import android.os.ShellCommand;
 import android.service.dataloader.DataLoaderService;
 import android.text.TextUtils;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import libcore.io.IoUtils;
 
-import java.io.File;
 import java.io.IOException;
+import java.lang.ref.WeakReference;
 import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
 import java.util.Collection;
 
 /**
@@ -37,41 +41,109 @@
 public class PackageManagerShellCommandDataLoader extends DataLoaderService {
     public static final String TAG = "PackageManagerShellCommandDataLoader";
 
-    static class DataLoader implements DataLoaderService.DataLoader {
-        private ParcelFileDescriptor mInFd = null;
-        private FileSystemConnector mConnector = null;
+    private static final String PACKAGE = "android";
+    private static final String CLASS = PackageManagerShellCommandDataLoader.class.getName();
 
-        private static final String STDIN_PATH = "-";
+    static final SecureRandom sRandom = new SecureRandom();
+    static final SparseArray<WeakReference<ShellCommand>> sShellCommands = new SparseArray<>();
+
+    private static final char ARGS_DELIM = '&';
+    private static final String SHELL_COMMAND_ID_PREFIX = "shellCommandId=";
+    private static final int INVALID_SHELL_COMMAND_ID = -1;
+    private static final int TOO_MANY_PENDING_SHELL_COMMANDS = 10;
+
+    private static final String STDIN_PATH = "-";
+
+    static DataLoaderParams getDataLoaderParams(ShellCommand shellCommand) {
+        int commandId;
+        synchronized (sShellCommands) {
+            // Clean up old references.
+            for (int i = sShellCommands.size() - 1; i >= 0; i--) {
+                WeakReference<ShellCommand> oldRef = sShellCommands.valueAt(i);
+                if (oldRef.get() == null) {
+                    sShellCommands.removeAt(i);
+                }
+            }
+
+            // Sanity check.
+            if (sShellCommands.size() > TOO_MANY_PENDING_SHELL_COMMANDS) {
+                Slog.e(TAG, "Too many pending shell commands: " + sShellCommands.size());
+            }
+
+            // Generate new id and put ref to the array.
+            do {
+                commandId = sRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
+            } while (sShellCommands.contains(commandId));
+
+            sShellCommands.put(commandId, new WeakReference<>(shellCommand));
+        }
+
+        final String args = SHELL_COMMAND_ID_PREFIX + commandId;
+        return DataLoaderParams.forStreaming(new ComponentName(PACKAGE, CLASS), args);
+    }
+
+    private static int extractShellCommandId(String args) {
+        int sessionIdIdx = args.indexOf(SHELL_COMMAND_ID_PREFIX);
+        if (sessionIdIdx < 0) {
+            Slog.e(TAG, "Missing shell command id param.");
+            return INVALID_SHELL_COMMAND_ID;
+        }
+        sessionIdIdx += SHELL_COMMAND_ID_PREFIX.length();
+        int delimIdx = args.indexOf(ARGS_DELIM, sessionIdIdx);
+        try {
+            if (delimIdx < 0) {
+                return Integer.parseInt(args.substring(sessionIdIdx));
+            } else {
+                return Integer.parseInt(args.substring(sessionIdIdx, delimIdx));
+            }
+        } catch (NumberFormatException e) {
+            Slog.e(TAG, "Incorrect shell command id format.", e);
+            return INVALID_SHELL_COMMAND_ID;
+        }
+    }
+
+    static class DataLoader implements DataLoaderService.DataLoader {
+        private DataLoaderParams mParams = null;
+        private FileSystemConnector mConnector = null;
 
         @Override
         public boolean onCreate(@NonNull DataLoaderParams dataLoaderParams,
                 @NonNull FileSystemConnector connector) {
+            mParams = dataLoaderParams;
             mConnector = connector;
             return true;
         }
+
         @Override
         public boolean onPrepareImage(Collection<InstallationFile> addedFiles,
                 Collection<String> removedFiles) {
+            final int commandId = extractShellCommandId(mParams.getArguments());
+            if (commandId == INVALID_SHELL_COMMAND_ID) {
+                return false;
+            }
+
+            final WeakReference<ShellCommand> shellCommandRef;
+            synchronized (sShellCommands) {
+                shellCommandRef = sShellCommands.get(commandId, null);
+            }
+            final ShellCommand shellCommand =
+                    shellCommandRef != null ? shellCommandRef.get() : null;
+            if (shellCommand == null) {
+                Slog.e(TAG, "Missing shell command.");
+                return false;
+            }
             try {
                 for (InstallationFile fileInfo : addedFiles) {
                     String filePath = new String(fileInfo.getMetadata(), StandardCharsets.UTF_8);
                     if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) {
-                        // TODO(b/146080380): add support for STDIN installations.
-                        if (mInFd == null) {
-                            Slog.e(TAG, "Invalid stdin file descriptor.");
-                            return false;
-                        }
-                        ParcelFileDescriptor inFd = ParcelFileDescriptor.dup(
-                                mInFd.getFileDescriptor());
+                        final ParcelFileDescriptor inFd = ParcelFileDescriptor.dup(
+                                shellCommand.getInFileDescriptor());
                         mConnector.writeData(fileInfo.getName(), 0, fileInfo.getSize(), inFd);
                     } else {
-                        File localFile = new File(filePath);
                         ParcelFileDescriptor incomingFd = null;
                         try {
-                            // TODO(b/146080380): open files via callback into shell command.
-                            incomingFd = ParcelFileDescriptor.open(localFile,
-                                    ParcelFileDescriptor.MODE_READ_ONLY);
-                            mConnector.writeData(fileInfo.getName(), 0, localFile.length(),
+                            incomingFd = shellCommand.openFileForSystem(filePath, "r");
+                            mConnector.writeData(fileInfo.getName(), 0, incomingFd.getStatSize(),
                                     incomingFd);
                         } finally {
                             IoUtils.closeQuietly(incomingFd);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 5a124a7..ea83adb 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -562,10 +562,6 @@
     private boolean mScreenshotChordPowerKeyTriggered;
     private long mScreenshotChordPowerKeyTime;
 
-    private static final long MOVING_DISPLAY_TO_TOP_DURATION_MILLIS = 10;
-    private volatile boolean mMovingDisplayToTopKeyTriggered;
-    private volatile long mMovingDisplayToTopKeyTime;
-
     // Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up
     private int mRingerToggleChord = VOLUME_HUSH_OFF;
 
@@ -633,7 +629,6 @@
     private static final int MSG_POWER_VERY_LONG_PRESS = 25;
     private static final int MSG_NOTIFY_USER_ACTIVITY = 26;
     private static final int MSG_RINGER_TOGGLE_CHORD = 27;
-    private static final int MSG_MOVE_DISPLAY_TO_TOP = 28;
 
     private class PolicyHandler extends Handler {
         @Override
@@ -697,7 +692,7 @@
                     accessibilityShortcutActivated();
                     break;
                 case MSG_BUGREPORT_TV:
-                    requestFullBugreport();
+                    requestFullBugreportOrLaunchHandlerApp();
                     break;
                 case MSG_ACCESSIBILITY_TV:
                     if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(false)) {
@@ -723,10 +718,6 @@
                 case MSG_RINGER_TOGGLE_CHORD:
                     handleRingerChordGesture();
                     break;
-                case MSG_MOVE_DISPLAY_TO_TOP:
-                    mWindowManagerFuncs.moveDisplayToTop(msg.arg1);
-                    mMovingDisplayToTopKeyTriggered = false;
-                    break;
             }
         }
     }
@@ -2545,36 +2536,6 @@
     @Override
     public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
             int policyFlags) {
-        final long result = interceptKeyBeforeDispatchingInner(focusedToken, event, policyFlags);
-        final int eventDisplayId = event.getDisplayId();
-        if (result == 0 && !mPerDisplayFocusEnabled
-                && eventDisplayId != INVALID_DISPLAY && eventDisplayId != mTopFocusedDisplayId) {
-            // An event is targeting a non-focused display. Try to move the display to top so that
-            // it can become the focused display to interact with the user.
-            final long eventDownTime = event.getDownTime();
-            if (mMovingDisplayToTopKeyTime < eventDownTime) {
-                // We have not handled this event yet. Move the display to top, and then tell
-                // dispatcher to try again later.
-                mMovingDisplayToTopKeyTime = eventDownTime;
-                mMovingDisplayToTopKeyTriggered = true;
-                mHandler.sendMessage(
-                        mHandler.obtainMessage(MSG_MOVE_DISPLAY_TO_TOP, eventDisplayId, 0));
-                return MOVING_DISPLAY_TO_TOP_DURATION_MILLIS;
-            } else if (mMovingDisplayToTopKeyTriggered) {
-                // The message has not been handled yet. Tell dispatcher to try again later.
-                return MOVING_DISPLAY_TO_TOP_DURATION_MILLIS;
-            }
-            // The target display is still not the top focused display. Drop the event because the
-            // display may not contain any window which can receive keys.
-            Slog.w(TAG, "Dropping key targeting non-focused display #" + eventDisplayId
-                    + " keyCode=" + KeyEvent.keyCodeToString(event.getKeyCode()));
-            return -1;
-        }
-        return result;
-    }
-
-    private long interceptKeyBeforeDispatchingInner(IBinder focusedToken, KeyEvent event,
-            int policyFlags) {
         final boolean keyguardOn = keyguardOn();
         final int keyCode = event.getKeyCode();
         final int repeatCount = event.getRepeatCount();
@@ -3061,12 +3022,14 @@
         return mAccessibilityTvScheduled;
     }
 
-    private void requestFullBugreport() {
+    private void requestFullBugreportOrLaunchHandlerApp() {
         if ("1".equals(SystemProperties.get("ro.debuggable"))
                 || Settings.Global.getInt(mContext.getContentResolver(),
                         Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1) {
             try {
-                ActivityManager.getService().requestFullBugReport();
+                if (!ActivityManager.getService().launchBugReportHandlerApp()) {
+                    ActivityManager.getService().requestFullBugReport();
+                }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error taking bugreport", e);
             }
@@ -3613,7 +3576,6 @@
         final boolean canceled = event.isCanceled();
         final int keyCode = event.getKeyCode();
         final int displayId = event.getDisplayId();
-
         final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
 
         // If screen is off then we treat the case where the keyguard is open but hidden
@@ -4035,6 +3997,23 @@
                     PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY");
         }
 
+        if ((result & ACTION_PASS_TO_USER) != 0) {
+            // If the key event is targeted to a specific display, then the user is interacting with
+            // that display. Therefore, give focus to the display that the user is interacting with.
+            if (!mPerDisplayFocusEnabled
+                    && displayId != INVALID_DISPLAY && displayId != mTopFocusedDisplayId) {
+                // An event is targeting a non-focused display. Move the display to top so that
+                // it can become the focused display to interact with the user.
+                // This should be done asynchronously, once the focus logic is fully moved to input
+                // from windowmanager. Currently, we need to ensure the setInputWindows completes,
+                // which would force the focus event to be queued before the current key event.
+                // TODO(b/70668286): post call to 'moveDisplayToTop' to mHandler instead
+                Log.i(TAG, "Moving non-focused display " + displayId + " to top "
+                        + "because a key is targeting it");
+                mWindowManagerFuncs.moveDisplayToTop(displayId);
+            }
+        }
+
         return result;
     }
 
@@ -5213,7 +5192,7 @@
                     final Intent dock = createHomeDockIntent();
                     if (dock != null) {
                         int result = ActivityTaskManager.getService()
-                                .startActivityAsUser(null, null, dock,
+                                .startActivityAsUser(null, mContext.getBasePackageName(), dock,
                                         dock.resolveTypeIfNeeded(mContext.getContentResolver()),
                                         null, null, 0,
                                         ActivityManager.START_FLAG_ONLY_IF_NEEDED,
@@ -5224,7 +5203,7 @@
                     }
                 }
                 int result = ActivityTaskManager.getService()
-                        .startActivityAsUser(null, null, mHomeIntent,
+                        .startActivityAsUser(null, mContext.getBasePackageName(), mHomeIntent,
                                 mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
                                 null, null, 0,
                                 ActivityManager.START_FLAG_ONLY_IF_NEEDED,
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index b4f4eca..f661b5e 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -22,7 +22,7 @@
 import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Intent;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
 
 import java.io.PrintWriter;
 
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 02656ea..da848d8 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -24,10 +24,10 @@
 import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Intent;
+import android.os.TimestampedValue;
 import android.telephony.TelephonyManager;
 import android.util.LocalLog;
 import android.util.Slog;
-import android.util.TimestampedValue;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 94c2192..23b94bd 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -751,10 +751,10 @@
         if (abort) {
             launchObserverNotifyActivityLaunchCancelled(info);
         } else {
-            logAppTransitionFinished(info);
             if (info.isInterestingToLoggerAndObserver()) {
                 launchObserverNotifyActivityLaunchFinished(info, timestampNs);
             }
+            logAppTransitionFinished(info);
         }
         info.mPendingDrawActivities.clear();
         mTransitionInfoList.remove(info);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 65d0c4c..26d76a8d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -243,9 +243,9 @@
 import android.app.servertransaction.PauseActivityItem;
 import android.app.servertransaction.PipModeChangeItem;
 import android.app.servertransaction.ResumeActivityItem;
+import android.app.servertransaction.StartActivityItem;
 import android.app.servertransaction.StopActivityItem;
 import android.app.servertransaction.TopResumedActivityChangeItem;
-import android.app.servertransaction.WindowVisibilityItem;
 import android.app.usage.UsageEvents.Event;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -4497,7 +4497,8 @@
             sleeping = false;
             app.postPendingUiCleanMsg(true);
             if (reportToClient) {
-                makeClientVisible();
+                mClientVisibilityDeferred = false;
+                makeActiveIfNeeded(starting);
             } else {
                 mClientVisibilityDeferred = true;
             }
@@ -4511,23 +4512,6 @@
         handleAlreadyVisible();
     }
 
-    /** Send visibility change message to the client and pause if needed. */
-    void makeClientVisible() {
-        mClientVisibilityDeferred = false;
-        try {
-            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                    WindowVisibilityItem.obtain(true /* showWindow */));
-            makeActiveIfNeeded(null /* activeActivity*/);
-            if (isState(STOPPING, STOPPED)) {
-                // Set state to STARTED in order to have consistent state with client while
-                // making an non-active activity visible from stopped.
-                setState(STARTED, "makeClientVisible");
-            }
-        } catch (Exception e) {
-            Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e);
-        }
-    }
-
     void makeInvisible() {
         if (!mVisibleRequested) {
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + this);
@@ -4556,14 +4540,6 @@
             switch (getState()) {
                 case STOPPING:
                 case STOPPED:
-                    if (attachedToProcess()) {
-                        if (DEBUG_VISIBILITY) {
-                            Slog.v(TAG_VISIBILITY, "Scheduling invisibility: " + this);
-                        }
-                        mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
-                                appToken, WindowVisibilityItem.obtain(false /* showWindow */));
-                    }
-
                     // Reset the flag indicating that an app can enter picture-in-picture once the
                     // activity is hidden
                     supportsEnterPipOnTaskSwitch = false;
@@ -4595,17 +4571,17 @@
     boolean makeActiveIfNeeded(ActivityRecord activeActivity) {
         if (shouldResumeActivity(activeActivity)) {
             if (DEBUG_VISIBILITY) {
-                Slog.v("TAG_VISIBILITY", "Resume visible activity, " + this);
+                Slog.v(TAG_VISIBILITY, "Resume visible activity, " + this);
             }
             return getActivityStack().resumeTopActivityUncheckedLocked(activeActivity /* prev */,
                     null /* options */);
         } else if (shouldPauseActivity(activeActivity)) {
             if (DEBUG_VISIBILITY) {
-                Slog.v("TAG_VISIBILITY", "Pause visible activity, " + this);
+                Slog.v(TAG_VISIBILITY, "Pause visible activity, " + this);
             }
             // An activity must be in the {@link PAUSING} state for the system to validate
             // the move to {@link PAUSED}.
-            setState(PAUSING, "makeVisibleIfNeeded");
+            setState(PAUSING, "makeActiveIfNeeded");
             try {
                 mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                         PauseActivityItem.obtain(finishing, false /* userLeaving */,
@@ -4613,6 +4589,17 @@
             } catch (Exception e) {
                 Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e);
             }
+        } else if (shouldStartActivity()) {
+            if (DEBUG_VISIBILITY) {
+                Slog.v(TAG_VISIBILITY, "Start visible activity, " + this);
+            }
+            setState(STARTED, "makeActiveIfNeeded");
+            try {
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                        StartActivityItem.obtain());
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e);
+            }
         }
         return false;
     }
@@ -4656,6 +4643,16 @@
     }
 
     /**
+     * Check if activity should be moved to STARTED state.
+     * NOTE: This will not check if activity should be made paused or resumed first, so it must only
+     * be called after checking with {@link #shouldResumeActivity(ActivityRecord)}
+     * and {@link #shouldPauseActivity(ActivityRecord)}.
+     */
+    private boolean shouldStartActivity() {
+        return mVisibleRequested && isState(STOPPED);
+    }
+
+    /**
      * Check if activity is eligible to be made active (resumed of paused). The activity:
      * - should be paused, stopped or stopping
      * - should not be the currently active one or launching behind other tasks
@@ -4890,16 +4887,13 @@
             }
             setState(STOPPING, "stopIfPossible");
             if (DEBUG_VISIBILITY) {
-                Slog.v(TAG_VISIBILITY, "Stopping visibleRequested="
-                        + mVisibleRequested + " for " + this);
-            }
-            if (!mVisibleRequested) {
-                setVisibility(false);
+                Slog.v(TAG_VISIBILITY, "Stopping:" + this);
             }
             EventLogTags.writeWmStopActivity(
                     mUserId, System.identityHashCode(this), shortComponentName);
             mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                    StopActivityItem.obtain(mVisibleRequested, configChangeFlags));
+                    StopActivityItem.obtain(configChangeFlags));
+
             if (stack.shouldSleepOrShutDownActivities()) {
                 setSleeping(true);
             }
@@ -6107,11 +6101,6 @@
                 // This also avoids if the next activity never reports idle (e.g. animating view),
                 // the previous will need to wait until idle timeout to be stopped or destroyed.
                 mStackSupervisor.scheduleProcessStoppingAndFinishingActivities();
-            } else {
-                // Instead of doing the full stop routine here, let's just hide any activities
-                // we now can, and let them stop when the normal idle happens.
-                mStackSupervisor.processStoppingActivities(null /* launchedActivity */,
-                        true /* onlyUpdateVisibility */, true /* unused */, null /* unused */);
             }
         }
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -7206,7 +7195,7 @@
         // {@link ActivityTaskManagerService.activityStopped}).
         try {
             mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                    StopActivityItem.obtain(false /* showWindow */, 0 /* configChanges */));
+                    StopActivityItem.obtain(0 /* configChanges */));
         } catch (RemoteException e) {
             Slog.w(TAG, "Exception thrown during restart " + this, e);
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index e8564fc..fd871bd 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2076,54 +2076,11 @@
             boolean processPausingActivities, String reason) {
         // Stop any activities that are scheduled to do so but have been waiting for the transition
         // animation to finish.
-        processStoppingActivities(launchedActivity, false /* onlyUpdateVisibility */,
-                processPausingActivities, reason);
-
-        final int numFinishingActivities = mFinishingActivities.size();
-        if (numFinishingActivities == 0) {
-            return;
-        }
-
-        // Finish any activities that are scheduled to do so but have been waiting for the next one
-        // to start.
-        final ArrayList<ActivityRecord> finishingActivities = new ArrayList<>(mFinishingActivities);
-        mFinishingActivities.clear();
-        for (int i = 0; i < numFinishingActivities; i++) {
-            final ActivityRecord r = finishingActivities.get(i);
-            if (r.isInHistory()) {
-                r.destroyImmediately(true /* removeFromApp */, "finish-" + reason);
-            }
-        }
-    }
-
-    /** Stop or destroy the stopping activities if they are not interactive. */
-    void processStoppingActivities(ActivityRecord launchedActivity, boolean onlyUpdateVisibility,
-            boolean processPausingActivities, String reason) {
-        final int numStoppingActivities = mStoppingActivities.size();
-        if (numStoppingActivities == 0) {
-            return;
-        }
         ArrayList<ActivityRecord> readyToStopActivities = null;
-
-        final boolean nowVisible = mRootWindowContainer.allResumedActivitiesVisible();
-        for (int activityNdx = numStoppingActivities - 1; activityNdx >= 0; --activityNdx) {
-            final ActivityRecord s = mStoppingActivities.get(activityNdx);
-            if (nowVisible && s.finishing) {
-
-                // If this activity is finishing, it is sitting on top of
-                // everyone else but we now know it is no longer needed...
-                // so get rid of it.  Otherwise, we need to go through the
-                // normal flow and hide it once we determine that it is
-                // hidden by the activities in front of it.
-                if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s);
-                s.setVisibility(false);
-            }
-            if (onlyUpdateVisibility) {
-                continue;
-            }
-
+        for (int i = mStoppingActivities.size() - 1; i >= 0; --i) {
+            final ActivityRecord s = mStoppingActivities.get(i);
             final boolean animating = s.isAnimating(TRANSITION);
-            if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
+            if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + s.nowVisible
                     + " animating=" + animating + " finishing=" + s.finishing);
 
             final ActivityStack stack = s.getActivityStack();
@@ -2145,7 +2102,7 @@
                 }
                 readyToStopActivities.add(s);
 
-                mStoppingActivities.remove(activityNdx);
+                mStoppingActivities.remove(i);
             }
         }
 
@@ -2161,6 +2118,22 @@
                 }
             }
         }
+
+        final int numFinishingActivities = mFinishingActivities.size();
+        if (numFinishingActivities == 0) {
+            return;
+        }
+
+        // Finish any activities that are scheduled to do so but have been waiting for the next one
+        // to start.
+        final ArrayList<ActivityRecord> finishingActivities = new ArrayList<>(mFinishingActivities);
+        mFinishingActivities.clear();
+        for (int i = 0; i < numFinishingActivities; i++) {
+            final ActivityRecord r = finishingActivities.get(i);
+            if (r.isInHistory()) {
+                r.destroyImmediately(true /* removeFromApp */, "finish-" + reason);
+            }
+        }
     }
 
     void removeHistoryRecords(WindowProcessController app) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 76c0e4e..474c5c9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -7203,7 +7203,8 @@
                             ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
                     if (mController != null) {
                         final long token = proto.start(CONTROLLER);
-                        proto.write(CONTROLLER, mController.toString());
+                        proto.write(ActivityManagerServiceDumpProcessesProto.Controller.CONTROLLER,
+                                mController.toString());
                         proto.write(IS_A_MONKEY, mControllerIsAMonkey);
                         proto.end(token);
                     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7f94445..ba9d757 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -205,6 +205,7 @@
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
+import android.view.IDisplayWindowInsetsController;
 import android.view.ISystemGestureExclusionListener;
 import android.view.IWindow;
 import android.view.InputChannel;
@@ -218,6 +219,7 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
 import android.view.View;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
 
@@ -570,6 +572,8 @@
      */
     WindowState mInputMethodTarget;
 
+    InsetsControlTarget mInputMethodControlTarget;
+
     /** If true hold off on modifying the animation layer of mInputMethodTarget */
     boolean mInputMethodTargetWaitingAnim;
 
@@ -598,6 +602,13 @@
     private final float mWindowCornerRadius;
 
     private final SparseArray<ShellRoot> mShellRoots = new SparseArray<>();
+    RemoteInsetsControlTarget mRemoteInsetsControlTarget = null;
+    private final IBinder.DeathRecipient mRemoteInsetsDeath =
+            () -> {
+                synchronized (mWmService.mGlobalLock) {
+                    mRemoteInsetsControlTarget = null;
+                }
+            };
 
     private RootWindowContainer mRootWindowContainer;
 
@@ -772,7 +783,11 @@
             // If this is the first layout, we need to initialize the last frames and inset values,
             // as otherwise we'd immediately cause an unnecessary resize.
             if (firstLayout) {
-                w.updateLastFrames();
+                // The client may compute its actual requested size according to the first layout,
+                // so we still request the window to resize if the current frame is empty.
+                if (!w.getFrameLw().isEmpty()) {
+                    w.updateLastFrames();
+                }
                 w.updateLastInsetValues();
                 w.updateLocationInParentDisplayIfNeeded();
             }
@@ -1152,6 +1167,22 @@
         mShellRoots.remove(windowType);
     }
 
+    void setRemoteInsetsController(IDisplayWindowInsetsController controller) {
+        if (mRemoteInsetsControlTarget != null) {
+            mRemoteInsetsControlTarget.mRemoteInsetsController.asBinder().unlinkToDeath(
+                    mRemoteInsetsDeath, 0);
+            mRemoteInsetsControlTarget = null;
+        }
+        if (controller != null) {
+            try {
+                controller.asBinder().linkToDeath(mRemoteInsetsDeath, 0);
+                mRemoteInsetsControlTarget = new RemoteInsetsControlTarget(controller);
+            } catch (RemoteException e) {
+                return;
+            }
+        }
+    }
+
     /** Changes the display the input window token is housed on to this one. */
     void reParentWindowToken(WindowToken token) {
         final DisplayContent prevDc = token.getDisplayContent();
@@ -3379,6 +3410,14 @@
         }
     }
 
+    boolean isImeAttachedToApp() {
+        return (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null
+                && mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+                // An activity with override bounds should be letterboxed inside its parent bounds,
+                // so it doesn't fill the screen.
+                && mInputMethodTarget.mActivityRecord.matchParentBounds());
+    }
+
     private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) {
         if (target == mInputMethodTarget && mInputMethodTargetWaitingAnim == targetWaitingAnim) {
             return;
@@ -3387,7 +3426,8 @@
         mInputMethodTarget = target;
         mInputMethodTargetWaitingAnim = targetWaitingAnim;
         assignWindowLayers(false /* setLayoutNeeded */);
-        mInsetsStateController.onImeTargetChanged(target);
+        mInputMethodControlTarget = computeImeControlTarget();
+        mInsetsStateController.onImeTargetChanged(mInputMethodControlTarget);
         updateImeParent();
     }
 
@@ -3412,11 +3452,7 @@
         // Attach it to app if the target is part of an app and such app is covering the entire
         // screen. If it's not covering the entire screen the IME might extend beyond the apps
         // bounds.
-        if (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null
-                && mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
-                // An activity with override bounds should be letterboxed inside its parent bounds,
-                // so it doesn't fill the screen.
-                && mInputMethodTarget.mActivityRecord.matchParentBounds()) {
+        if (isImeAttachedToApp()) {
             return mInputMethodTarget.mActivityRecord.getSurfaceControl();
         }
 
@@ -3424,6 +3460,19 @@
         return mWindowContainers.getSurfaceControl();
     }
 
+    /**
+     * Computes which control-target the IME should be attached to.
+     */
+    @VisibleForTesting
+    InsetsControlTarget computeImeControlTarget() {
+        if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) {
+            return mRemoteInsetsControlTarget;
+        }
+
+        // Otherwise, we just use the ime target
+        return mInputMethodTarget;
+    }
+
     void setLayoutNeeded() {
         if (DEBUG_LAYOUT) Slog.w(TAG_WM, "setLayoutNeeded: callers=" + Debug.getCallers(3));
         mLayoutNeeded = true;
@@ -6684,4 +6733,50 @@
     Context getDisplayUiContext() {
         return mDisplayPolicy.getSystemUiContext();
     }
+
+    class RemoteInsetsControlTarget implements InsetsControlTarget {
+        private final IDisplayWindowInsetsController mRemoteInsetsController;
+
+        RemoteInsetsControlTarget(IDisplayWindowInsetsController controller) {
+            mRemoteInsetsController = controller;
+        }
+
+        void notifyInsetsChanged() {
+            try {
+                mRemoteInsetsController.insetsChanged(
+                        getInsetsStateController().getRawInsetsState());
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to deliver inset state change", e);
+            }
+        }
+
+        @Override
+        public void notifyInsetsControlChanged() {
+            final InsetsStateController stateController = getInsetsStateController();
+            try {
+                mRemoteInsetsController.insetsControlChanged(stateController.getRawInsetsState(),
+                        stateController.getControlsForDispatch(this));
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to deliver inset state change", e);
+            }
+        }
+
+        @Override
+        public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
+            try {
+                mRemoteInsetsController.showInsets(types, fromIme);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to deliver showInsets", e);
+            }
+        }
+
+        @Override
+        public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
+            try {
+                mRemoteInsetsController.hideInsets(types, fromIme);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to deliver showInsets", e);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 55f5e28..c09834f 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -79,12 +79,13 @@
 
         final PooledConsumer f = PooledLambda.obtainConsumer(
                 EnsureActivitiesVisibleHelper::setActivityVisibilityState, this,
-                PooledLambda.__(ActivityRecord.class), resumeTopActivity);
+                PooledLambda.__(ActivityRecord.class), starting, resumeTopActivity);
         mContiner.forAllActivities(f);
         f.recycle();
     }
 
-    private void setActivityVisibilityState(ActivityRecord r, final boolean resumeTopActivity) {
+    private void setActivityVisibilityState(ActivityRecord r, ActivityRecord starting,
+            final boolean resumeTopActivity) {
         final boolean isTop = r == mTop;
         if (mAboveTop && !isTop) {
             return;
@@ -129,7 +130,8 @@
                         "Skipping: already visible at " + r);
 
                 if (r.mClientVisibilityDeferred && mNotifyClients) {
-                    r.makeClientVisible();
+                    r.makeActiveIfNeeded(r.mClientVisibilityDeferred ? null : starting);
+                    r.mClientVisibilityDeferred = false;
                 }
 
                 r.handleAlreadyVisible();
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 05ede21..fb97ecf 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -69,7 +69,7 @@
         mShowImeRunner = () -> {
             // Target should still be the same.
             if (isImeTargetFromDisplayContentAndImeSame()) {
-                mDisplayContent.mInputMethodTarget.showInsets(
+                mDisplayContent.mInputMethodControlTarget.showInsets(
                         WindowInsets.Type.ime(), true /* fromIme */);
             }
             abortShowImePostLayout();
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 184e7d6..a13383d 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -70,6 +70,9 @@
      */
     private boolean mServerVisible;
 
+    private boolean mSeamlessRotating;
+    private long mFinishSeamlessRotateFrameNumber = -1;
+
     private final boolean mControllable;
 
     InsetsSourceProvider(InsetsSource source, InsetsStateController stateController,
@@ -170,7 +173,9 @@
         updateSourceFrame();
         if (mControl != null) {
             final Rect frame = mWin.getWindowFrames().mFrame;
-            if (mControl.setSurfacePosition(frame.left, frame.top)) {
+            if (mControl.setSurfacePosition(frame.left, frame.top) && mControlTarget != null) {
+                // The leash has been stale, we need to create a new one for the client.
+                updateControlForTarget(mControlTarget, true /* force */);
                 mStateController.notifyControlChanged(mControlTarget);
             }
         }
@@ -189,6 +194,11 @@
     }
 
     void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
+        if (mSeamlessRotating) {
+            // We are un-rotating the window against the display rotation. We don't want the target
+            // to control the window for now.
+            return;
+        }
         if (mWin == null) {
             mControlTarget = target;
             return;
@@ -203,14 +213,42 @@
         }
         mAdapter = new ControlAdapter();
         setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
-        mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter,
-                !mClientVisible /* hidden */);
+        final Transaction t = mDisplayContent.getPendingTransaction();
+        mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */);
+        final SurfaceControl leash = mAdapter.mCapturedLeash;
+        final long frameNumber = mFinishSeamlessRotateFrameNumber;
+        mFinishSeamlessRotateFrameNumber = -1;
+        if (frameNumber >= 0 && mWin.mHasSurface && leash != null) {
+            // We just finished the seamless rotation. We don't want to change the position or the
+            // window crop of the surface controls (including the leash) until the client finishes
+            // drawing the new frame of the new orientation. Although we cannot defer the reparent
+            // operation, it is fine, because reparent won't cause any visual effect.
+            final SurfaceControl barrier = mWin.mWinAnimator.mSurfaceController.mSurfaceControl;
+            t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber);
+            t.deferTransactionUntil(leash, barrier, frameNumber);
+        }
         mControlTarget = target;
-        mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash,
+        mControl = new InsetsSourceControl(mSource.getType(), leash,
                 new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top));
     }
 
-    boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) {
+    void startSeamlessRotation() {
+        if (!mSeamlessRotating) {
+            mSeamlessRotating = true;
+
+            // This will revoke the leash and clear the control target.
+            mWin.cancelAnimation();
+        }
+    }
+
+    void finishSeamlessRotation(boolean timeout) {
+        if (mSeamlessRotating) {
+            mSeamlessRotating = false;
+            mFinishSeamlessRotateFrameNumber = timeout ? -1 : mWin.getFrameNumber();
+        }
+    }
+
+    boolean onInsetsModified(InsetsControlTarget caller, InsetsSource modifiedSource) {
         if (mControlTarget != caller || modifiedSource.isVisible() == mClientVisible) {
             return false;
         }
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 1526074..b2234d1 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -91,6 +91,10 @@
         return state;
     }
 
+    InsetsState getRawInsetsState() {
+        return mState;
+    }
+
     @Nullable InsetsSourceControl[] getControlsForDispatch(InsetsControlTarget target) {
         ArrayList<Integer> controlled = mControlTargetTypeMap.get(target);
         if (controlled == null) {
@@ -144,7 +148,7 @@
         getImeSourceProvider().onPostInsetsDispatched();
     }
 
-    void onInsetsModified(WindowState windowState, InsetsState state) {
+    void onInsetsModified(InsetsControlTarget windowState, InsetsState state) {
         boolean changed = false;
         for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
             final InsetsSource source = state.sourceAt(i);
@@ -199,7 +203,7 @@
         if (target == previous) {
             return;
         }
-        final InsetsSourceProvider provider = getSourceProvider(type);
+        final InsetsSourceProvider provider = mProviders.get(type);
         if (provider == null) {
             return;
         }
@@ -207,6 +211,7 @@
             return;
         }
         provider.updateControlForTarget(target, false /* force */);
+        target = provider.getControlTarget();
         if (previous != null) {
             removeFromControlMaps(previous, type, false /* fake */);
             mPendingControlChanged.add(previous);
@@ -296,6 +301,9 @@
 
     void notifyInsetsChanged() {
         mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
+        if (mDisplayContent.mRemoteInsetsControlTarget != null) {
+            mDisplayContent.mRemoteInsetsControlTarget.notifyInsetsChanged();
+        }
     }
 
     void dump(String prefix, PrintWriter pw) {
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 399c5d3..eaa0ea7 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -16,8 +16,12 @@
 
 package com.android.server.wm;
 
+import static com.android.server.wm.AnimationSpecProto.ROTATE;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
+import static com.android.server.wm.RotationAnimationSpecProto.DURATION_MS;
+import static com.android.server.wm.RotationAnimationSpecProto.END_LUMA;
+import static com.android.server.wm.RotationAnimationSpecProto.START_LUMA;
 import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING;
 import static com.android.server.wm.ScreenRotationAnimationProto.STARTED;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -25,7 +29,9 @@
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
 
+import android.animation.ArgbEvaluator;
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -40,7 +46,9 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Transformation;
 
+import com.android.internal.R;
 import com.android.server.protolog.common.ProtoLog;
+import com.android.server.wm.utils.RotationAnimationUtils;
 
 import java.io.PrintWriter;
 
@@ -60,10 +68,10 @@
  *      animation first rotate the new content into the old orientation to then be able to
  *      animate to the new orientation
  *
- * <li> The exiting Blackframe: <p>
- *     Because the change of orientation might change the width and height of the content (i.e
- *     when rotating from portrait to landscape) we "crop" the new content using black frames
- *     around the screenshot so the new content does not go beyond the screenshot's bounds
+ * <li> The Background color frame: <p>
+ *      To have the animation seem more seamless, we add a color transitioning background behind the
+ *      exiting and entering layouts. We compute the brightness of the start and end
+ *      layouts and transition from the two brightness values as grayscale underneath the animation
  *
  * <li> The entering Blackframe: <p>
  *     The enter Blackframe is similar to the exit Blackframe but is only used when a custom
@@ -81,8 +89,6 @@
      */
     private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
     private static final int SCREEN_FREEZE_LAYER_ENTER = SCREEN_FREEZE_LAYER_BASE;
-    private static final int SCREEN_FREEZE_LAYER_SCREENSHOT = SCREEN_FREEZE_LAYER_BASE + 1;
-    private static final int SCREEN_FREEZE_LAYER_EXIT = SCREEN_FREEZE_LAYER_BASE + 2;
 
     private final Context mContext;
     private final DisplayContent mDisplayContent;
@@ -90,16 +96,18 @@
     private final Transformation mRotateExitTransformation = new Transformation();
     private final Transformation mRotateEnterTransformation = new Transformation();
     // Complete transformations being applied.
-    private final Transformation mExitTransformation = new Transformation();
     private final Transformation mEnterTransformation = new Transformation();
-    private final Matrix mFrameInitialMatrix = new Matrix();
     private final Matrix mSnapshotInitialMatrix = new Matrix();
-    private final Matrix mSnapshotFinalMatrix = new Matrix();
-    private final Matrix mExitFrameFinalMatrix = new Matrix();
     private final WindowManagerService mService;
+    /** Only used for custom animations and not screen rotation. */
     private SurfaceControl mEnterBlackFrameLayer;
-    private SurfaceControl mRotationLayer;
-    private SurfaceControl mSurfaceControl;
+    /** This layer contains the actual screenshot that is to be faded out. */
+    private SurfaceControl mScreenshotLayer;
+    /**
+     * Only used for screen rotation and not custom animations. Layered behind all other layers
+     * to avoid showing any "empty" spots
+     */
+    private SurfaceControl mBackColorSurface;
     private BlackFrame mEnteringBlackFrame;
     private int mWidth, mHeight;
 
@@ -120,8 +128,11 @@
     private boolean mFinishAnimReady;
     private long mFinishAnimStartTime;
     private boolean mForceDefaultOrientation;
-    private BlackFrame mExitingBlackFrame;
     private SurfaceRotationAnimationController mSurfaceRotationAnimationController;
+    /** Intensity of light/whiteness of the layout before rotation occurs. */
+    private float mStartLuma;
+    /** Intensity of light/whiteness of the layout after rotation occurs. */
+    private float mEndLuma;
 
     public ScreenRotationAnimation(Context context, DisplayContent displayContent,
             boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
@@ -162,9 +173,15 @@
 
         final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
         try {
-            mRotationLayer = displayContent.makeOverlay()
+            mBackColorSurface = displayContent.makeChildSurface(null)
+                    .setName("BackColorSurface")
+                    .setColorLayer()
+                    .build();
+
+            mScreenshotLayer = displayContent.makeOverlay()
                     .setName("RotationLayer")
-                    .setContainerLayer()
+                    .setBufferSize(mWidth, mHeight)
+                    .setSecure(isSecure)
                     .build();
 
             mEnterBlackFrameLayer = displayContent.makeOverlay()
@@ -172,26 +189,21 @@
                     .setContainerLayer()
                     .build();
 
-            mSurfaceControl = mService.makeSurfaceBuilder(null)
-                    .setName("ScreenshotSurface")
-                    .setParent(mRotationLayer)
-                    .setBufferSize(mWidth, mHeight)
-                    .setSecure(isSecure)
-                    .build();
-
             // In case display bounds change, screenshot buffer and surface may mismatch so set a
             // scaling mode.
             SurfaceControl.Transaction t2 = mService.mTransactionFactory.get();
-            t2.setOverrideScalingMode(mSurfaceControl, Surface.SCALING_MODE_SCALE_TO_WINDOW);
+            t2.setOverrideScalingMode(mScreenshotLayer, Surface.SCALING_MODE_SCALE_TO_WINDOW);
             t2.apply(true /* sync */);
 
             // Capture a screenshot into the surface we just created.
             final int displayId = display.getDisplayId();
             final Surface surface = mService.mSurfaceFactory.get();
-            surface.copyFrom(mSurfaceControl);
+            surface.copyFrom(mScreenshotLayer);
             SurfaceControl.ScreenshotGraphicBuffer gb =
                     mService.mDisplayManagerInternal.screenshot(displayId);
             if (gb != null) {
+                mStartLuma = RotationAnimationUtils.getAvgBorderLuma(gb.getGraphicBuffer(),
+                        gb.getColorSpace());
                 try {
                     surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(),
                             gb.getColorSpace());
@@ -202,13 +214,15 @@
                 // screenshot surface we display it in also has FLAG_SECURE so that
                 // the user can not screenshot secure layers via the screenshot surface.
                 if (gb.containsSecureLayers()) {
-                    t.setSecure(mSurfaceControl, true);
+                    t.setSecure(mScreenshotLayer, true);
                 }
-                t.setLayer(mRotationLayer, SCREEN_FREEZE_LAYER_BASE);
-                t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
-                t.setAlpha(mSurfaceControl, 0);
-                t.show(mRotationLayer);
-                t.show(mSurfaceControl);
+                t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
+                t.reparent(mBackColorSurface, displayContent.getSurfaceControl());
+                t.setLayer(mBackColorSurface, -1);
+                t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
+                t.setAlpha(mBackColorSurface, 1);
+                t.show(mScreenshotLayer);
+                t.show(mBackColorSurface);
             } else {
                 Slog.w(TAG, "Unable to take screenshot of display " + displayId);
             }
@@ -218,32 +232,11 @@
         }
 
         ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
-                    "  FREEZE %s: CREATE", mSurfaceControl);
+                    "  FREEZE %s: CREATE", mScreenshotLayer);
         setRotation(t, originalRotation);
         t.apply();
     }
 
-    private static void createRotationMatrix(int rotation, int width, int height,
-            Matrix outMatrix) {
-        switch (rotation) {
-            case Surface.ROTATION_0:
-                outMatrix.reset();
-                break;
-            case Surface.ROTATION_90:
-                outMatrix.setRotate(90, 0, 0);
-                outMatrix.postTranslate(height, 0);
-                break;
-            case Surface.ROTATION_180:
-                outMatrix.setRotate(180, 0, 0);
-                outMatrix.postTranslate(width, height);
-                break;
-            case Surface.ROTATION_270:
-                outMatrix.setRotate(270, 0, 0);
-                outMatrix.postTranslate(0, width);
-                break;
-        }
-    }
-
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(STARTED, mStarted);
@@ -252,11 +245,11 @@
     }
 
     boolean hasScreenshot() {
-        return mSurfaceControl != null;
+        return mScreenshotLayer != null;
     }
 
     private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) {
-        if (mRotationLayer == null) {
+        if (mScreenshotLayer == null) {
             return;
         }
         matrix.getValues(mTmpFloats);
@@ -267,24 +260,19 @@
             x -= mCurrentDisplayRect.left;
             y -= mCurrentDisplayRect.top;
         }
-        t.setPosition(mRotationLayer, x, y);
-        t.setMatrix(mRotationLayer,
+        t.setPosition(mScreenshotLayer, x, y);
+        t.setMatrix(mScreenshotLayer,
                 mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
                 mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
 
-        t.setAlpha(mSurfaceControl, (float) 1.0);
-        t.setAlpha(mRotationLayer, (float) 1.0);
-        t.show(mRotationLayer);
+        t.setAlpha(mScreenshotLayer, (float) 1.0);
+        t.show(mScreenshotLayer);
     }
 
     public void printTo(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("mSurface="); pw.print(mSurfaceControl);
+        pw.print(prefix); pw.print("mSurface="); pw.print(mScreenshotLayer);
         pw.print(" mWidth="); pw.print(mWidth);
         pw.print(" mHeight="); pw.println(mHeight);
-        pw.print(prefix); pw.print("mExitingBlackFrame="); pw.println(mExitingBlackFrame);
-        if (mExitingBlackFrame != null) {
-            mExitingBlackFrame.printTo(prefix + "  ", pw);
-        }
         pw.print(prefix);
         pw.print("mEnteringBlackFrame=");
         pw.println(mEnteringBlackFrame);
@@ -303,20 +291,10 @@
         pw.print(" "); mRotateExitTransformation.printShortString(pw); pw.println();
         pw.print(prefix); pw.print("mRotateEnterAnimation="); pw.print(mRotateEnterAnimation);
         pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println();
-        pw.print(prefix); pw.print("mExitTransformation=");
-        mExitTransformation.printShortString(pw); pw.println();
         pw.print(prefix); pw.print("mEnterTransformation=");
         mEnterTransformation.printShortString(pw); pw.println();
-        pw.print(prefix); pw.print("mFrameInitialMatrix=");
-        mFrameInitialMatrix.printShortString(pw);
-        pw.println();
         pw.print(prefix); pw.print("mSnapshotInitialMatrix=");
-        mSnapshotInitialMatrix.printShortString(pw);
-        pw.print(" mSnapshotFinalMatrix="); mSnapshotFinalMatrix.printShortString(pw);
-        pw.println();
-        pw.print(prefix); pw.print("mExitFrameFinalMatrix=");
-        mExitFrameFinalMatrix.printShortString(pw);
-        pw.println();
+        mSnapshotInitialMatrix.printShortString(pw);pw.println();
         pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation);
         if (mForceDefaultOrientation) {
             pw.print(" mOriginalDisplayRect="); pw.print(mOriginalDisplayRect.toShortString());
@@ -331,7 +309,7 @@
         // to the snapshot to make it stay in the same original position
         // with the current screen rotation.
         int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0);
-        createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
+        RotationAnimationUtils.createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
 
         setRotationTransform(t, mSnapshotInitialMatrix);
     }
@@ -341,7 +319,7 @@
      */
     private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration,
             float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
-        if (mSurfaceControl == null) {
+        if (mScreenshotLayer == null) {
             // Can't do animation.
             return false;
         }
@@ -354,89 +332,58 @@
         // Figure out how the screen has moved from the original rotation.
         int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation);
 
-        mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,
-                com.android.internal.R.anim.screen_rotate_alpha);
 
         final boolean customAnim;
         if (exitAnim != 0 && enterAnim != 0) {
             customAnim = true;
             mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, exitAnim);
             mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, enterAnim);
+            mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,
+                    R.anim.screen_rotate_alpha);
         } else {
             customAnim = false;
-            switch (delta) {
+            switch (delta) { /* Counter-Clockwise Rotations */
                 case Surface.ROTATION_0:
                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
-                            com.android.internal.R.anim.screen_rotate_0_exit);
+                            R.anim.screen_rotate_0_exit);
                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
-                            com.android.internal.R.anim.screen_rotate_0_enter);
+                            R.anim.screen_rotate_0_enter);
                     break;
                 case Surface.ROTATION_90:
                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
-                            com.android.internal.R.anim.screen_rotate_plus_90_exit);
+                            R.anim.screen_rotate_plus_90_exit);
                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
-                            com.android.internal.R.anim.screen_rotate_plus_90_enter);
+                            R.anim.screen_rotate_plus_90_enter);
                     break;
                 case Surface.ROTATION_180:
                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
-                            com.android.internal.R.anim.screen_rotate_180_exit);
+                            R.anim.screen_rotate_180_exit);
                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
-                            com.android.internal.R.anim.screen_rotate_180_enter);
+                            R.anim.screen_rotate_180_enter);
                     break;
                 case Surface.ROTATION_270:
                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
-                            com.android.internal.R.anim.screen_rotate_minus_90_exit);
+                            R.anim.screen_rotate_minus_90_exit);
                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
-                            com.android.internal.R.anim.screen_rotate_minus_90_enter);
+                            R.anim.screen_rotate_minus_90_enter);
                     break;
             }
         }
 
-        // Initialize the animations.  This is a hack, redefining what "parent"
-        // means to allow supplying the last and next size.  In this definition
-        // "%p" is the original (let's call it "previous") size, and "%" is the
-        // screen's current/new size.
-        mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
         mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
+        mRotateExitAnimation.restrictDuration(maxAnimationDuration);
+        mRotateExitAnimation.scaleCurrentDuration(animationScale);
+        mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
+        mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
+        mRotateEnterAnimation.scaleCurrentDuration(animationScale);
+
         mAnimRunning = false;
         mFinishAnimReady = false;
         mFinishAnimStartTime = -1;
 
-        mRotateExitAnimation.restrictDuration(maxAnimationDuration);
-        mRotateExitAnimation.scaleCurrentDuration(animationScale);
-        mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
-        mRotateEnterAnimation.scaleCurrentDuration(animationScale);
-        mRotateAlphaAnimation.restrictDuration(maxAnimationDuration);
-        mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
-
-        if (!customAnim && mExitingBlackFrame == null) {
-            try {
-                // Compute the transformation matrix that must be applied
-                // the the black frame to make it stay in the initial position
-                // before the new screen rotation.  This is different than the
-                // snapshot transformation because the snapshot is always based
-                // of the native orientation of the screen, not the orientation
-                // we were last in.
-                createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
-
-                final Rect outer;
-                final Rect inner;
-                if (mForceDefaultOrientation) {
-                    // Going from a smaller Display to a larger Display, add curtains to sides
-                    // or top and bottom. Going from a larger to smaller display will result in
-                    // no BlackSurfaces being constructed.
-                    outer = mCurrentDisplayRect;
-                    inner = mOriginalDisplayRect;
-                } else {
-                    outer = new Rect(-mWidth, -mHeight, mWidth * 2, mHeight * 2);
-                    inner = new Rect(0, 0, mWidth, mHeight);
-                }
-                mExitingBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
-                        SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation,
-                        mRotationLayer);
-            } catch (OutOfResourcesException e) {
-                Slog.w(TAG, "Unable to allocate black surface", e);
-            }
+        if (customAnim) {
+            mRotateAlphaAnimation.restrictDuration(maxAnimationDuration);
+            mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
         }
 
         if (customAnim && mEnteringBlackFrame == null) {
@@ -451,7 +398,12 @@
             }
         }
 
-        mSurfaceRotationAnimationController.startAnimation();
+        if (customAnim) {
+            mSurfaceRotationAnimationController.startCustomAnimation();
+        } else {
+            mSurfaceRotationAnimationController.startScreenRotationAnimation();
+        }
+
         return true;
     }
 
@@ -460,11 +412,13 @@
      */
     public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration,
             float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
-        if (mSurfaceControl == null) {
+        if (mScreenshotLayer == null) {
             // Can't do animation.
             return false;
         }
         if (!mStarted) {
+            mEndLuma = RotationAnimationUtils.getLumaOfSurfaceControl(mDisplayContent.getDisplay(),
+                    mDisplayContent.getWindowingLayer());
             startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight,
                     exitAnim, enterAnim);
         }
@@ -480,28 +434,28 @@
             mSurfaceRotationAnimationController.cancel();
             mSurfaceRotationAnimationController = null;
         }
-        if (mSurfaceControl != null) {
-            ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "  FREEZE %s: DESTROY", mSurfaceControl);
-            mSurfaceControl = null;
+
+        if (mScreenshotLayer != null) {
+            ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "  FREEZE %s: DESTROY", mScreenshotLayer);
             SurfaceControl.Transaction t = mService.mTransactionFactory.get();
-            if (mRotationLayer != null) {
-                if (mRotationLayer.isValid()) {
-                    t.remove(mRotationLayer);
-                }
-                mRotationLayer = null;
+            if (mScreenshotLayer.isValid()) {
+                t.remove(mScreenshotLayer);
             }
+            mScreenshotLayer = null;
+
             if (mEnterBlackFrameLayer != null) {
                 if (mEnterBlackFrameLayer.isValid()) {
                     t.remove(mEnterBlackFrameLayer);
                 }
                 mEnterBlackFrameLayer = null;
             }
+            if (mBackColorSurface != null) {
+                t.remove(mBackColorSurface);
+                mBackColorSurface = null;
+            }
             t.apply();
         }
-        if (mExitingBlackFrame != null) {
-            mExitingBlackFrame.kill();
-            mExitingBlackFrame = null;
-        }
+
         if (mEnteringBlackFrame != null) {
             mEnteringBlackFrame.kill();
             mEnteringBlackFrame = null;
@@ -537,18 +491,28 @@
      * Utility class that runs a {@link ScreenRotationAnimation} on the {@link
      * SurfaceAnimationRunner}.
      * <p>
-     * The rotation animation is divided into the following hierarchy:
+     * The rotation animation supports both screen rotation and custom animations
+     *
+     * For custom animations:
      * <ul>
-     * <li> A first rotation layer, containing the blackframes. This layer is animated by the
-     * "screen_rotate_X_exit" that applies a scale and rotate and where X is value of the rotation.
-     *     <ul>
-     *         <li> A child layer containing the screenshot on which is added an animation of it's
-     *     alpha channel ("screen_rotate_alpha") and that will rotate with his parent layer.</li>
-     *     </ul>
-     * <li> A second rotation layer used when custom animations are passed in
+     *   <li>
+     *     The screenshot layer which has an added animation of it's alpha channel
+     *     ("screen_rotate_alpha") and that will be applied along with the custom animation.
+     *   </li>
+     *   <li> A device layer that is animated with the provided custom animation </li>
+     * </ul>
+     *
+     * For screen rotation:
+     * <ul>
+     *   <li> A rotation layer that is both rotated and faded out during a single animation </li>
+     *   <li> A device layer that is both rotated and faded in during a single animation </li>
+     *   <li> A background color layer that transitions colors behind the first two layers </li>
+     * </ul>
+     *
      * {@link ScreenRotationAnimation#startAnimation(
      *     SurfaceControl.Transaction, long, float, int, int, int, int)}.
      * </ul>
+     *
      * <p>
      * Thus an {@link LocalAnimationAdapter.AnimationSpec} is created for each of
      * this three {@link SurfaceControl}s which then delegates the animation to the
@@ -556,22 +520,35 @@
      */
     class SurfaceRotationAnimationController {
         private SurfaceAnimator mDisplayAnimator;
-        private SurfaceAnimator mEnterBlackFrameAnimator;
         private SurfaceAnimator mScreenshotRotationAnimator;
         private SurfaceAnimator mRotateScreenAnimator;
+        private SurfaceAnimator mEnterBlackFrameAnimator;
+
+        void startCustomAnimation() {
+            try {
+                mService.mSurfaceAnimationRunner.deferStartingAnimations();
+                mRotateScreenAnimator = startScreenshotAlphaAnimation();
+                mDisplayAnimator = startDisplayRotation();
+                if (mEnteringBlackFrame != null) {
+                    mEnterBlackFrameAnimator = startEnterBlackFrameAnimation();
+                }
+            } finally {
+                mService.mSurfaceAnimationRunner.continueStartingAnimations();
+            }
+        }
 
         /**
          * Start the rotation animation of the display and the screenshot on the
          * {@link SurfaceAnimationRunner}.
          */
-        void startAnimation() {
-            mRotateScreenAnimator = startScreenshotAlphaAnimation();
-            mDisplayAnimator = startDisplayRotation();
-            if (mExitingBlackFrame != null) {
+        void startScreenRotationAnimation() {
+            try {
+                mService.mSurfaceAnimationRunner.deferStartingAnimations();
+                mDisplayAnimator = startDisplayRotation();
                 mScreenshotRotationAnimator = startScreenshotRotationAnimation();
-            }
-            if (mEnteringBlackFrame != null) {
-                mEnterBlackFrameAnimator = startEnterBlackFrameAnimation();
+                startColorAnimation();
+            } finally {
+                mService.mSurfaceAnimationRunner.continueStartingAnimations();
             }
         }
 
@@ -596,8 +573,8 @@
 
         private SurfaceAnimator startScreenshotAlphaAnimation() {
             return startAnimation(initializeBuilder()
-                            .setSurfaceControl(mSurfaceControl)
-                            .setAnimationLeashParent(mRotationLayer)
+                            .setSurfaceControl(mScreenshotLayer)
+                            .setAnimationLeashParent(mDisplayContent.getOverlayLayer())
                             .setWidth(mWidth)
                             .setHeight(mHeight)
                             .build(),
@@ -616,13 +593,67 @@
 
         private SurfaceAnimator startScreenshotRotationAnimation() {
             return startAnimation(initializeBuilder()
-                            .setSurfaceControl(mRotationLayer)
+                            .setSurfaceControl(mScreenshotLayer)
                             .setAnimationLeashParent(mDisplayContent.getOverlayLayer())
                             .build(),
                     createWindowAnimationSpec(mRotateExitAnimation),
                     this::onAnimationEnd);
         }
 
+
+        /**
+         * Applies the color change from {@link #mStartLuma} to {@link #mEndLuma} as a
+         * grayscale color
+         */
+        private void startColorAnimation() {
+            int colorTransitionMs = mContext.getResources().getInteger(
+                    R.integer.config_screen_rotation_color_transition);
+            final SurfaceAnimationRunner runner = mService.mSurfaceAnimationRunner;
+            final float[] rgbTmpFloat = new float[3];
+            final int startColor = Color.rgb(mStartLuma, mStartLuma, mStartLuma);
+            final int endColor = Color.rgb(mEndLuma, mEndLuma, mEndLuma);
+            final long duration = colorTransitionMs * (long) mService.getCurrentAnimatorScale();
+            final ArgbEvaluator va = ArgbEvaluator.getInstance();
+            runner.startAnimation(
+                new LocalAnimationAdapter.AnimationSpec() {
+                    @Override
+                    public long getDuration() {
+                        return duration;
+                    }
+
+                    @Override
+                    public void apply(SurfaceControl.Transaction t, SurfaceControl leash,
+                        long currentPlayTime) {
+                        float fraction = (float)currentPlayTime / (float)getDuration();
+                        int color = (Integer) va.evaluate(fraction, startColor, endColor);
+                        Color middleColor = Color.valueOf(color);
+                        rgbTmpFloat[0] = middleColor.red();
+                        rgbTmpFloat[1] = middleColor.green();
+                        rgbTmpFloat[2] = middleColor.blue();
+                        if (leash.isValid()) {
+                            t.setColor(leash, rgbTmpFloat);
+                        }
+                    }
+
+                    @Override
+                    public void dump(PrintWriter pw, String prefix) {
+                        pw.println(prefix + "startLuma=" + mStartLuma
+                                + " endLuma=" + mEndLuma
+                                + " durationMs=" + colorTransitionMs);
+                    }
+
+                    @Override
+                    public void dumpDebugInner(ProtoOutputStream proto) {
+                        final long token = proto.start(ROTATE);
+                        proto.write(START_LUMA, mStartLuma);
+                        proto.write(END_LUMA, mEndLuma);
+                        proto.write(DURATION_MS, colorTransitionMs);
+                        proto.end(token);
+                    }
+                },
+                mBackColorSurface, mDisplayContent.getPendingTransaction(), null);
+        }
+
         private WindowAnimationSpec createWindowAnimationSpec(Animation mAnimation) {
             return new WindowAnimationSpec(mAnimation, new Point(0, 0) /* position */,
                     false /* canSkipFirstFrame */, 0 /* WindowCornerRadius */);
@@ -646,7 +677,6 @@
 
             LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter(
                     animationSpec, mService.mSurfaceAnimationRunner);
-
             animator.startAnimation(mDisplayContent.getPendingTransaction(),
                     localAnimationAdapter, false);
             return animator;
@@ -692,7 +722,6 @@
             if (mEnterBlackFrameAnimator != null) {
                 mEnterBlackFrameAnimator.cancelAnimation();
             }
-
             if (mScreenshotRotationAnimator != null) {
                 mScreenshotRotationAnimator.cancelAnimation();
             }
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 50cea2e..5633b6b 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -78,6 +78,10 @@
     @GuardedBy("mLock")
     private boolean mAnimationStartDeferred;
 
+    /**
+     * There should only ever be one instance of this class. Usual spot for it is with
+     * {@link WindowManagerService}
+     */
     SurfaceAnimationRunner(Supplier<Transaction> transactionFactory,
             PowerManagerInternal powerManagerInternal) {
         this(null /* callbackProvider */, null /* animatorFactory */,
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 976730e..5286a6e 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -322,12 +322,16 @@
         if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
         final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
                 .setParent(mAnimatable.getAnimationLeashParent())
+                .setHidden(hidden)
                 .setName(surface + " - animation-leash");
         final SurfaceControl leash = builder.build();
         t.setWindowCrop(leash, width, height);
+
+        // TODO: rely on builder.setHidden(hidden) instead of show and setAlpha when b/138459974 is
+        //       fixed.
         t.show(leash);
-        // TODO: change this back to use show instead of alpha when b/138459974 is fixed.
         t.setAlpha(leash, hidden ? 0 : 1);
+
         t.reparent(surface, leash);
         return leash;
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 223e9b9..74fdba1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -205,6 +205,7 @@
 import android.view.Gravity;
 import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.IDisplayFoldListener;
+import android.view.IDisplayWindowInsetsController;
 import android.view.IDisplayWindowListener;
 import android.view.IDisplayWindowRotationController;
 import android.view.IDockedStackListener;
@@ -2768,6 +2769,7 @@
                         true /* includingParents */);
             }
         }
+        syncInputTransactions();
     }
 
     /**
@@ -3727,6 +3729,48 @@
     }
 
     @Override
+    public void setDisplayWindowInsetsController(
+            int displayId, IDisplayWindowInsetsController insetsController) {
+        if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS);
+        }
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent dc = mRoot.getDisplayContent(displayId);
+                if (dc == null) {
+                    return;
+                }
+                dc.setRemoteInsetsController(insetsController);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void modifyDisplayWindowInsets(int displayId, InsetsState state) {
+        if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS);
+        }
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent dc = mRoot.getDisplayContent(displayId);
+                if (dc == null || dc.mRemoteInsetsControlTarget == null) {
+                    return;
+                }
+                dc.getInsetsStateController().onInsetsModified(
+                        dc.mRemoteInsetsControlTarget, state);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
     public int watchRotation(IRotationWatcher watcher, int displayId) {
         final DisplayContent displayContent;
         synchronized (mGlobalLock) {
@@ -7314,7 +7358,8 @@
                     // If there was a pending IME show(), reset it as IME has been
                     // requested to be hidden.
                     dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout();
-                    dc.mInputMethodTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */);
+                    dc.mInputMethodControlTarget.hideInsets(WindowInsets.Type.ime(),
+                            true /* fromIme */);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4e768da..ba40f62 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -688,6 +688,9 @@
         }
 
         if (mForceSeamlesslyRotate || requested) {
+            if (mControllableInsetProvider != null) {
+                mControllableInsetProvider.startSeamlessRotation();
+            }
             mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo());
             mPendingSeamlessRotate.unrotate(transaction, this);
             getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
@@ -702,6 +705,9 @@
             mPendingSeamlessRotate = null;
             getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
                     false /* seamlesslyRotated */);
+            if (mControllableInsetProvider != null) {
+                mControllableInsetProvider.finishSeamlessRotation(timeout);
+            }
         }
     }
 
@@ -792,8 +798,9 @@
         mSeq = seq;
         mPowerManagerWrapper = powerManagerWrapper;
         mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
-        mRequestedInsetsState =
-                getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this);
+        mRequestedInsetsState = new InsetsState(
+                getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this),
+                true /* copySources */);
         if (DEBUG) {
             Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
                             + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
diff --git a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
new file mode 100644
index 0000000..94f6676
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
@@ -0,0 +1,97 @@
+/*
+ * 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.server.wm.utils;
+
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
+import android.graphics.Matrix;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+
+/** Helper functions for the {@link com.android.server.wm.ScreenRotationAnimation} class*/
+public class RotationAnimationUtils {
+
+    /**
+     * Converts the provided {@link GraphicBuffer} and converts it to a bitmap to then sample the
+     * luminance at the borders of the bitmap
+     * @return the average luminance of all the pixels at the borders of the bitmap
+     */
+    public static float getAvgBorderLuma(GraphicBuffer graphicBuffer, ColorSpace colorSpace) {
+        Bitmap hwBitmap = Bitmap.wrapHardwareBuffer(graphicBuffer, colorSpace);
+        if (hwBitmap == null) {
+            return 0;
+        }
+
+        Bitmap swaBitmap = hwBitmap.copy(Bitmap.Config.ARGB_8888, false);
+        float totalLuma = 0;
+        int height = swaBitmap.getHeight();
+        int width = swaBitmap.getWidth();
+        int i;
+        for (i = 0; i < width; i++) {
+            totalLuma += swaBitmap.getColor(i, 0).luminance();
+            totalLuma += swaBitmap.getColor(i, height - 1).luminance();
+        }
+        for (i = 0; i < height; i++) {
+            totalLuma += swaBitmap.getColor(0, i).luminance();
+            totalLuma += swaBitmap.getColor(width - 1, i).luminance();
+        }
+        return totalLuma / (2 * width + 2 * height);
+    }
+
+    /**
+     * Gets the average border luma by taking a screenshot of the {@param surfaceControl}.
+     * @see #getAvgBorderLuma(GraphicBuffer, ColorSpace)
+     */
+    public static float getLumaOfSurfaceControl(Display display, SurfaceControl surfaceControl) {
+        if (surfaceControl ==  null) {
+            return 0;
+        }
+
+        Point size = new Point();
+        display.getSize(size);
+        Rect crop = new Rect(0, 0, size.x, size.y);
+        SurfaceControl.ScreenshotGraphicBuffer buffer =
+                SurfaceControl.captureLayers(surfaceControl, crop, 1);
+        return RotationAnimationUtils.getAvgBorderLuma(buffer.getGraphicBuffer(),
+                buffer.getColorSpace());
+    }
+
+    public static void createRotationMatrix(int rotation, int width, int height, Matrix outMatrix) {
+        switch (rotation) {
+            case Surface.ROTATION_0:
+                outMatrix.reset();
+                break;
+            case Surface.ROTATION_90:
+                outMatrix.setRotate(90, 0, 0);
+                outMatrix.postTranslate(height, 0);
+                break;
+            case Surface.ROTATION_180:
+                outMatrix.setRotate(180, 0, 0);
+                outMatrix.postTranslate(width, height);
+                break;
+            case Surface.ROTATION_270:
+                outMatrix.setRotate(270, 0, 0);
+                outMatrix.postTranslate(0, width);
+                break;
+        }
+    }
+}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 2e8e5e7..c0891d7 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1757,6 +1757,12 @@
     return im->getInputManager()->getReader()->canDispatchToDisplay(deviceId, displayId);
 }
 
+static void nativeNotifyPortAssociationsChanged(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+    im->getInputManager()->getReader()->requestRefreshConfiguration(
+            InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gInputManagerMethods[] = {
@@ -1842,6 +1848,8 @@
             (void*) nativeSetCustomPointerIcon },
     { "nativeCanDispatchToDisplay", "(JII)Z",
             (void*) nativeCanDispatchToDisplay },
+    { "nativeNotifyPortAssociationsChanged", "(J)V",
+            (void*) nativeNotifyPortAssociationsChanged },
 };
 
 #define FIND_CLASS(var, className) \
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 5a1d552..8641059 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -64,4 +64,8 @@
     }
 
     public void setLocationEnabled(ComponentName who, boolean locationEnabled) {}
+
+    public boolean isOrganizationOwnedDeviceWithManagedProfile() {
+        return false;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 13246ba..2a08f5c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8607,6 +8607,24 @@
     }
 
     @Override
+    public boolean isOrganizationOwnedDeviceWithManagedProfile() {
+        if (!mHasFeature) {
+            return false;
+        }
+        enforceManageUsers();
+
+        return mInjector.binderWithCleanCallingIdentity(() -> {
+            for (UserInfo ui : mUserManager.getUsers()) {
+                if (ui.isManagedProfile() && isProfileOwnerOfOrganizationOwnedDevice(ui.id)) {
+                    return true;
+                }
+            }
+
+            return false;
+        });
+    }
+
+    @Override
     public boolean checkDeviceIdentifierAccess(String packageName, int pid, int uid) {
         ensureCallerIdentityMatchesIfNotSystem(packageName, pid, uid);
 
@@ -14584,8 +14602,10 @@
             for (int i = 0; i < users.length; i++) {
                 final ComponentName componentName = getProfileOwner(users[i]);
                 if (componentName != null) {
-                    admins.add(getActiveAdminForCallerLocked(
-                            componentName, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER));
+                    ActiveAdmin admin = getActiveAdminUncheckedLocked(componentName, users[i]);
+                    if (admin != null) {
+                        admins.add(admin);
+                    }
                 }
             }
             return admins;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index bfec51c..b6a8ca4 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -751,6 +751,7 @@
         // Now that we have the bare essentials of the OS up and running, take
         // note that we just booted, which might send out a rescue party if
         // we're stuck in a runtime restart loop.
+        RescueParty.registerHealthObserver(mSystemContext);
         RescueParty.noteBoot(mSystemContext);
 
         // Manages LEDs and display backlight so we need it to bring up the display.
diff --git a/services/robotests/src/com/android/server/location/LocationRequestStatisticsTest.java b/services/robotests/src/com/android/server/location/LocationRequestStatisticsTest.java
new file mode 100644
index 0000000..4cbdbd17
--- /dev/null
+++ b/services/robotests/src/com/android/server/location/LocationRequestStatisticsTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.location;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Unit tests for {@link LocationRequestStatistics}.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class LocationRequestStatisticsTest {
+
+    /**
+     * Check adding and removing requests & strings
+     */
+    @Test
+    public void testRequestSummary() {
+        LocationRequestStatistics.RequestSummary summary =
+                new LocationRequestStatistics.RequestSummary(
+                "com.example", "gps", 1000);
+        StringWriter stringWriter = new StringWriter();
+        summary.dump(new IndentingPrintWriter(new PrintWriter(stringWriter), "  "), 1234);
+        assertThat(stringWriter.toString()).startsWith("At");
+
+        StringWriter stringWriterRemove = new StringWriter();
+        summary = new LocationRequestStatistics.RequestSummary(
+                "com.example", "gps",
+                LocationRequestStatistics.RequestSummary.REQUEST_ENDED_INTERVAL);
+        summary.dump(new IndentingPrintWriter(new PrintWriter(stringWriterRemove), "  "), 2345);
+        assertThat(stringWriterRemove.toString()).contains("-");
+    }
+
+    /**
+     * Check summary list size capping
+     */
+    @Test
+    public void testSummaryList() {
+        LocationRequestStatistics statistics = new LocationRequestStatistics();
+        statistics.history.addRequest("com.example", "gps", 1000);
+        assertThat(statistics.history.mList.size()).isEqualTo(1);
+        // Try (not) to overflow
+        for (int i = 0; i < LocationRequestStatistics.RequestSummaryLimitedHistory.MAX_SIZE; i++) {
+            statistics.history.addRequest("com.example", "gps", 1000);
+        }
+        assertThat(statistics.history.mList.size()).isEqualTo(
+                LocationRequestStatistics.RequestSummaryLimitedHistory.MAX_SIZE);
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index 0d6020c..30d89d3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -32,6 +32,7 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.VersionedPackage;
 import android.os.RecoverySystem;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -39,6 +40,8 @@
 import android.provider.Settings;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
+import com.android.server.RescueParty.RescuePartyObserver;
 import com.android.server.am.SettingsToPropertiesMapper;
 import com.android.server.utils.FlagNamespaceUtils;
 
@@ -57,13 +60,15 @@
  * Test RescueParty.
  */
 public class RescuePartyTest {
-    private static final int PERSISTENT_APP_UID = 12;
     private static final long CURRENT_NETWORK_TIME_MILLIS = 0L;
     private static final String FAKE_NATIVE_NAMESPACE1 = "native1";
     private static final String FAKE_NATIVE_NAMESPACE2 = "native2";
     private static final String[] FAKE_RESET_NATIVE_NAMESPACES =
             {FAKE_NATIVE_NAMESPACE1, FAKE_NATIVE_NAMESPACE2};
 
+    private static VersionedPackage sFailingPackage = new VersionedPackage("com.package.name", 1);
+    private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
+
     private MockitoSession mSession;
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -182,25 +187,25 @@
 
     @Test
     public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() {
-        notePersistentAppCrash(RescueParty.TRIGGER_COUNT);
+        notePersistentAppCrash();
 
         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
 
-        notePersistentAppCrash(RescueParty.TRIGGER_COUNT);
+        notePersistentAppCrash();
 
         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES);
         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
 
-        notePersistentAppCrash(RescueParty.TRIGGER_COUNT);
+        notePersistentAppCrash();
 
         verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS);
         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
 
-        notePersistentAppCrash(RescueParty.TRIGGER_COUNT);
+        notePersistentAppCrash();
 
         verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
         assertEquals(RescueParty.LEVEL_FACTORY_RESET,
@@ -221,20 +226,6 @@
     }
 
     @Test
-    public void testPersistentAppCrashDetectionWithWrongInterval() {
-        notePersistentAppCrash(RescueParty.TRIGGER_COUNT - 1);
-
-        // last persistent app crash is just outside of the boot loop detection window
-        doReturn(CURRENT_NETWORK_TIME_MILLIS
-                + RescueParty.PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS + 1)
-                .when(() -> RescueParty.getElapsedRealtime());
-        notePersistentAppCrash(/*numTimes=*/1);
-
-        assertEquals(RescueParty.LEVEL_NONE,
-                SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
-    }
-
-    @Test
     public void testBootLoopDetectionWithProperInterval() {
         noteBoot(RescueParty.TRIGGER_COUNT - 1);
 
@@ -249,21 +240,6 @@
     }
 
     @Test
-    public void testPersistentAppCrashDetectionWithProperInterval() {
-        notePersistentAppCrash(RescueParty.TRIGGER_COUNT - 1);
-
-        // last persistent app crash is just inside of the boot loop detection window
-        doReturn(CURRENT_NETWORK_TIME_MILLIS
-                + RescueParty.PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS)
-                .when(() -> RescueParty.getElapsedRealtime());
-        notePersistentAppCrash(/*numTimes=*/1);
-
-        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
-        assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
-                SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
-    }
-
-    @Test
     public void testBootLoopDetectionWithWrongTriggerCount() {
         noteBoot(RescueParty.TRIGGER_COUNT - 1);
         assertEquals(RescueParty.LEVEL_NONE,
@@ -271,13 +247,6 @@
     }
 
     @Test
-    public void testPersistentAppCrashDetectionWithWrongTriggerCount() {
-        notePersistentAppCrash(RescueParty.TRIGGER_COUNT - 1);
-        assertEquals(RescueParty.LEVEL_NONE,
-                SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
-    }
-
-    @Test
     public void testIsAttemptingFactoryReset() {
         noteBoot(RescueParty.TRIGGER_COUNT * 4);
 
@@ -319,6 +288,77 @@
                         FAKE_NATIVE_NAMESPACE2, /*makeDefault=*/true));
     }
 
+    @Test
+    public void testExplicitlyEnablingAndDisablingRescue() {
+        SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
+        SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true));
+        assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
+                PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), false);
+
+        SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
+        assertTrue(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
+                PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING));
+    }
+
+    @Test
+    public void testHealthCheckLevels() {
+        RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
+
+        // Ensure that no action is taken for cases where the failure reason is unknown
+        SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+                RescueParty.LEVEL_FACTORY_RESET));
+        assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN),
+                PackageHealthObserverImpact.USER_IMPACT_NONE);
+
+        /*
+        For the following cases, ensure that the returned user impact corresponds with the user
+        impact of the next available rescue level, not the current one.
+         */
+        SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+                RescueParty.LEVEL_NONE));
+        assertEquals(observer.onHealthCheckFailed(null,
+                PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
+                PackageHealthObserverImpact.USER_IMPACT_LOW);
+
+        SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+                RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS));
+        assertEquals(observer.onHealthCheckFailed(null,
+                PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
+                PackageHealthObserverImpact.USER_IMPACT_LOW);
+
+
+        SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+                RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES));
+        assertEquals(observer.onHealthCheckFailed(null,
+                PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
+                PackageHealthObserverImpact.USER_IMPACT_HIGH);
+
+
+        SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+                RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS));
+        assertEquals(observer.onHealthCheckFailed(null,
+                PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
+                PackageHealthObserverImpact.USER_IMPACT_HIGH);
+
+
+        SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+                RescueParty.LEVEL_FACTORY_RESET));
+        assertEquals(observer.onHealthCheckFailed(null,
+                PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
+                PackageHealthObserverImpact.USER_IMPACT_HIGH);
+    }
+
+    @Test
+    public void testRescueLevelIncrementsWhenExecuted() {
+        RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
+        SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+                RescueParty.LEVEL_NONE));
+        observer.execute(sFailingPackage,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH);
+        assertEquals(SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, -1),
+                RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS);
+    }
+
     private void verifySettingsResets(int resetMode) {
         verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null,
                 resetMode, UserHandle.USER_SYSTEM));
@@ -332,9 +372,8 @@
         }
     }
 
-    private void notePersistentAppCrash(int numTimes) {
-        for (int i = 0; i < numTimes; i++) {
-            RescueParty.noteAppCrash(mMockContext, PERSISTENT_APP_UID);
-        }
+    private void notePersistentAppCrash() {
+        RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage(
+                "com.package.name", 1), PackageWatchdog.FAILURE_REASON_UNKNOWN);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index d11d987..591c3a3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -85,6 +85,7 @@
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 
 import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
new file mode 100644
index 0000000..75239db
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.server.accessibility;
+
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.Manifest;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * APCT tests for {@link AccessibilityManagerService}.
+ */
+public class AccessibilityManagerServiceTest extends AndroidTestCase {
+    private static final String TAG = "A11Y_MANAGER_SERVICE_TEST";
+    private static final int ACTION_ID = 20;
+    private static final String LABEL = "label";
+    private static final String INTENT_ACTION = "TESTACTION";
+    private static final String DESCRIPTION = "description";
+    private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast(
+            InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION), 0);
+    private static final RemoteAction TEST_ACTION = new RemoteAction(
+            Icon.createWithContentUri("content://test"),
+            LABEL,
+            DESCRIPTION,
+            TEST_PENDING_INTENT);
+    private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION =
+            new AccessibilityAction(ACTION_ID, LABEL);
+
+    @Mock private PackageManager mMockPackageManager;
+    @Mock private WindowManagerInternal mMockWindowManagerService;
+    @Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
+    @Mock private SystemActionPerformer mMockSystemActionPerformer;
+    @Mock private AccessibilityWindowManager mMockA11yWindowManager;
+    @Mock private AccessibilityDisplayListener mMockA11yDisplayListener;
+    @Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
+
+    private AccessibilityManagerService mA11yms;
+
+    @Override
+    protected void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        LocalServices.removeServiceForTest(WindowManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+        LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerService);
+        LocalServices.addService(
+                ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
+        mA11yms = new AccessibilityManagerService(
+            InstrumentationRegistry.getContext(),
+            mMockPackageManager,
+            mMockSecurityPolicy,
+            mMockSystemActionPerformer,
+            mMockA11yWindowManager,
+            mMockA11yDisplayListener);
+    }
+
+    @SmallTest
+    public void testRegisterSystemActionWithoutPermission() throws Exception {
+        doThrow(SecurityException.class).when(mMockSecurityPolicy).enforceCallingPermission(
+                Manifest.permission.MANAGE_ACCESSIBILITY,
+                AccessibilityManagerService.FUNCTION_REGISTER_SYSTEM_ACTION);
+
+        try {
+            mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
+            fail();
+        } catch (SecurityException expected) {
+        }
+        verify(mMockSystemActionPerformer, never()).registerSystemAction(ACTION_ID, TEST_ACTION);
+    }
+
+    @SmallTest
+    public void testRegisterSystemAction() throws Exception {
+        mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
+        verify(mMockSystemActionPerformer).registerSystemAction(ACTION_ID, TEST_ACTION);
+    }
+
+    @SmallTest
+    public void testUnregisterSystemActionWithoutPermission() throws Exception {
+        doThrow(SecurityException.class).when(mMockSecurityPolicy).enforceCallingPermission(
+                Manifest.permission.MANAGE_ACCESSIBILITY,
+                AccessibilityManagerService.FUNCTION_UNREGISTER_SYSTEM_ACTION);
+
+        try {
+            mA11yms.unregisterSystemAction(ACTION_ID);
+            fail();
+        } catch (SecurityException expected) {
+        }
+        verify(mMockSystemActionPerformer, never()).unregisterSystemAction(ACTION_ID);
+    }
+
+    @SmallTest
+    public void testUnregisterSystemAction() throws Exception {
+        mA11yms.unregisterSystemAction(ACTION_ID);
+        verify(mMockSystemActionPerformer).unregisterSystemAction(ACTION_ID);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 99dd9a1..2ce70b6f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -44,6 +44,7 @@
 import android.testing.DexmakerShareClassLoaderRule;
 import android.view.Display;
 
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 67075ed..9db5a08 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -53,6 +53,7 @@
 import android.view.accessibility.IAccessibilityInteractionConnection;
 
 import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
index 44a514f..96ae102e5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
@@ -31,6 +31,8 @@
 import android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 
+import com.android.server.accessibility.test.MessageCapturingHandler;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
index de7bc44..30d00ad 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
@@ -31,6 +31,7 @@
 import android.view.KeyEvent;
 
 import com.android.server.accessibility.FingerprintGestureDispatcher.FingerprintGestureClient;
+import com.android.server.accessibility.test.MessageCapturingHandler;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index 23ce483..4123556 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -44,6 +44,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.accessibility.KeyEventDispatcher.KeyEventFilter;
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.policy.WindowManagerPolicy;
 
 import org.hamcrest.Description;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
index 322653b..78e651b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
@@ -33,6 +33,7 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.policy.WindowManagerPolicy;
 
 import org.hamcrest.Description;
@@ -212,4 +213,4 @@
             description.appendText("Matches key event");
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
index 773b877..82c6498 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
@@ -47,6 +47,7 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index 36e854c..1ac4a8e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -54,6 +54,7 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.accessibility.utils.MotionEventMatcher;
 
 import org.hamcrest.Description;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 8da927d..c8baca6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -37,6 +37,7 @@
 import android.os.IBinder;
 import android.view.accessibility.AccessibilityEvent;
 
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
index d4182f3..5a1ad86 100644
--- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
@@ -672,6 +672,30 @@
                 connectionTime2, mKeyStore.getLastConnectionTime(TEST_KEY_2));
     }
 
+    @Test
+    public void testClearAuthorizationsBeforeAdbEnabled() throws Exception {
+        // The adb key store is not instantiated until adb is enabled; however if the user attempts
+        // to clear the adb authorizations when adb is disabled after a boot a NullPointerException
+        // was thrown as deleteKeyStore is invoked against the key store. This test ensures the
+        // key store can be successfully cleared when adb is disabled.
+        mHandler = mManager.new AdbDebuggingHandler(FgThread.get().getLooper());
+
+        clearKeyStore();
+    }
+
+    @Test
+    public void testClearAuthorizationsDeletesKeyFiles() throws Exception {
+        mAdbKeyFile.createNewFile();
+        mAdbKeyXmlFile.createNewFile();
+
+        clearKeyStore();
+
+        assertFalse("The adb key file should have been deleted after revocation of the grants",
+                mAdbKeyFile.exists());
+        assertFalse("The adb xml key file should have been deleted after revocation of the grants",
+                mAdbKeyXmlFile.exists());
+    }
+
     /**
      * Runs an adb test with the provided configuration.
      *
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index f3c76b6..8871348 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -28,6 +28,7 @@
 
 import android.app.admin.DevicePolicyManagerInternal;
 import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetManagerInternal;
 import android.appwidget.AppWidgetProviderInfo;
 import android.appwidget.PendingHostUpdate;
 import android.content.BroadcastReceiver;
@@ -80,6 +81,7 @@
         super.setUp();
         LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
         LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
+        LocalServices.removeServiceForTest(AppWidgetManagerInternal.class);
 
         mTestContext = new TestContext();
         mPkgName = mTestContext.getOpPackageName();
diff --git a/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java b/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java
new file mode 100644
index 0000000..d0767cc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.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.server.compat;
+
+import android.content.pm.ApplicationInfo;
+
+class ApplicationInfoBuilder {
+    private boolean mIsDebuggable;
+    private int mTargetSdk;
+    private String mPackageName;
+
+    private ApplicationInfoBuilder() {
+        mTargetSdk = -1;
+    }
+
+    static ApplicationInfoBuilder create() {
+        return new ApplicationInfoBuilder();
+    }
+
+    ApplicationInfoBuilder withTargetSdk(int targetSdk) {
+        mTargetSdk = targetSdk;
+        return this;
+    }
+
+    ApplicationInfoBuilder debuggable() {
+        mIsDebuggable = true;
+        return this;
+    }
+
+    ApplicationInfoBuilder withPackageName(String packageName) {
+        mPackageName = packageName;
+        return this;
+    }
+
+    ApplicationInfo build() {
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        if (mIsDebuggable) {
+            applicationInfo.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
+        }
+        applicationInfo.packageName = mPackageName;
+        applicationInfo.targetSdkVersion = mTargetSdk;
+        return applicationInfo;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
new file mode 100644
index 0000000..328c71d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -0,0 +1,99 @@
+/*
+ * 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.server.compat;
+
+import android.content.Context;
+
+import com.android.internal.compat.AndroidBuildClassifier;
+
+import java.util.ArrayList;
+
+/**
+ * Helper class for creating a CompatConfig.
+ */
+class CompatConfigBuilder {
+    private ArrayList<CompatChange> mChanges;
+    private AndroidBuildClassifier mBuildClassifier;
+    private Context mContext;
+
+    private CompatConfigBuilder(AndroidBuildClassifier buildClassifier, Context context) {
+        mChanges = new ArrayList<>();
+        mBuildClassifier = buildClassifier;
+        mContext = context;
+    }
+
+    static CompatConfigBuilder create(AndroidBuildClassifier buildClassifier, Context context) {
+        return new CompatConfigBuilder(buildClassifier, context);
+    }
+
+    CompatConfigBuilder addTargetSdkChangeWithId(int sdk, long id) {
+        mChanges.add(new CompatChange(id, "", sdk, false, ""));
+        return this;
+    }
+
+    CompatConfigBuilder addTargetSdkDisabledChangeWithId(int sdk, long id) {
+        mChanges.add(new CompatChange(id, "", sdk, true, ""));
+        return this;
+    }
+
+    CompatConfigBuilder addTargetSdkChangeWithIdAndName(int sdk, long id, String name) {
+        mChanges.add(new CompatChange(id, name, sdk, false, ""));
+        return this;
+    }
+
+    CompatConfigBuilder addTargetSdkChangeWithIdAndDescription(int sdk, long id,
+            String description) {
+        mChanges.add(new CompatChange(id, "", sdk, false, description));
+        return this;
+    }
+
+    CompatConfigBuilder addEnabledChangeWithId(long id) {
+        mChanges.add(new CompatChange(id, "", -1, false, ""));
+        return this;
+    }
+
+    CompatConfigBuilder addEnabledChangeWithIdAndName(long id, String name) {
+        mChanges.add(new CompatChange(id, name, -1, false, ""));
+        return this;
+    }
+    CompatConfigBuilder addEnabledChangeWithIdAndDescription(long id, String description) {
+        mChanges.add(new CompatChange(id, "", -1, false, description));
+        return this;
+    }
+
+    CompatConfigBuilder addDisabledChangeWithId(long id) {
+        mChanges.add(new CompatChange(id, "", -1, true, ""));
+        return this;
+    }
+
+    CompatConfigBuilder addDisabledChangeWithIdAndName(long id, String name) {
+        mChanges.add(new CompatChange(id, name, -1, true, ""));
+        return this;
+    }
+
+    CompatConfigBuilder addDisabledChangeWithIdAndDescription(long id, String description) {
+        mChanges.add(new CompatChange(id, "", -1, true, description));
+        return this;
+    }
+
+    CompatConfig build() {
+        CompatConfig config = new CompatConfig(mBuildClassifier, mContext);
+        for (CompatChange change : mChanges) {
+            config.addChange(change);
+        }
+        return config;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index cb99c11..407f67e 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -18,12 +18,25 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.compat.AndroidBuildClassifier;
+
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -34,12 +47,12 @@
 @RunWith(AndroidJUnit4.class)
 public class CompatConfigTest {
 
-    private ApplicationInfo makeAppInfo(String pName, int targetSdkVersion) {
-        ApplicationInfo ai = new ApplicationInfo();
-        ai.packageName = pName;
-        ai.targetSdkVersion = targetSdkVersion;
-        return ai;
-    }
+    @Mock
+    private Context mContext;
+    @Mock
+    PackageManager mPackageManager;
+    @Mock
+    private AndroidBuildClassifier mBuildClassifier;
 
     private File createTempDir() {
         String base = System.getProperty("java.io.tmpdir");
@@ -54,112 +67,206 @@
         os.close();
     }
 
-    @Test
-    public void testUnknownChangeEnabled() {
-        CompatConfig pc = new CompatConfig();
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isTrue();
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        // Assume userdebug/eng non-final build
+        when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
+        when(mBuildClassifier.isFinalBuild()).thenReturn(false);
     }
 
     @Test
-    public void testDisabledChangeDisabled() {
-        CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, ""));
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
+    public void testUnknownChangeEnabled() throws Exception {
+        CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+        assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create().build()))
+            .isTrue();
     }
 
     @Test
-    public void testTargetSdkChangeDisabled() {
-        CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, null));
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse();
+    public void testDisabledChangeDisabled() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1234L)
+                .build();
+
+        assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create().build()))
+            .isFalse();
     }
 
     @Test
-    public void testTargetSdkChangeEnabled() {
-        CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, ""));
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
+    public void testTargetSdkChangeDisabled() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addTargetSdkChangeWithId(2, 1234L)
+                .build();
+
+        assertThat(compatConfig.isChangeEnabled(1234L,
+            ApplicationInfoBuilder.create().withTargetSdk(2).build()))
+            .isFalse();
     }
 
     @Test
-    public void testDisabledOverrideTargetSdkChange() {
-        CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null));
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isFalse();
+    public void testTargetSdkChangeEnabled() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addTargetSdkChangeWithId(2, 1234L)
+                .build();
+
+        assertThat(compatConfig.isChangeEnabled(1234L,
+            ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue();
     }
 
     @Test
-    public void testGetDisabledChanges() {
-        CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null));
-        pc.addChange(new CompatChange(2345L, "OTHER_CHANGE", -1, false, null));
-        assertThat(pc.getDisabledChanges(
-                makeAppInfo("com.some.package", 2))).asList().containsExactly(1234L);
+    public void testDisabledOverrideTargetSdkChange() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addTargetSdkDisabledChangeWithId(2, 1234L)
+                .build();
+
+        assertThat(compatConfig.isChangeEnabled(1234L,
+            ApplicationInfoBuilder.create().withTargetSdk(3).build())).isFalse();
     }
 
     @Test
-    public void testGetDisabledChangesSorted() {
-        CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null));
-        pc.addChange(new CompatChange(123L, "OTHER_CHANGE", 2, true, null));
-        pc.addChange(new CompatChange(12L, "THIRD_CHANGE", 2, true, null));
-        assertThat(pc.getDisabledChanges(
-                makeAppInfo("com.some.package", 2))).asList().containsExactly(12L, 123L, 1234L);
+    public void testGetDisabledChanges() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1234L)
+                .addEnabledChangeWithId(2345L)
+                .build();
+
+        assertThat(compatConfig.getDisabledChanges(
+            ApplicationInfoBuilder.create().build())).asList().containsExactly(1234L);
     }
 
     @Test
-    public void testPackageOverrideEnabled() {
-        CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null)); // disabled
-        pc.addOverride(1234L, "com.some.package", true);
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isTrue();
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isFalse();
+    public void testGetDisabledChangesSorted() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1234L)
+                .addDisabledChangeWithId(123L)
+                .addDisabledChangeWithId(12L)
+                .build();
+
+        assertThat(compatConfig.getDisabledChanges(ApplicationInfoBuilder.create().build()))
+            .asList().containsExactly(12L, 123L, 1234L);
     }
 
     @Test
-    public void testPackageOverrideDisabled() {
-        CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
-        pc.addOverride(1234L, "com.some.package", false);
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse();
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isTrue();
+    public void testPackageOverrideEnabled() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1234L)
+                .build();
+
+        compatConfig.addOverride(1234L, "com.some.package", true);
+
+        assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create()
+                .withPackageName("com.some.package").build())).isTrue();
+        assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create()
+                .withPackageName("com.other.package").build())).isFalse();
     }
 
     @Test
-    public void testPackageOverrideUnknownPackage() {
-        CompatConfig pc = new CompatConfig();
-        pc.addOverride(1234L, "com.some.package", false);
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse();
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isTrue();
+    public void testPackageOverrideDisabled() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addEnabledChangeWithId(1234L)
+                .build();
+
+        compatConfig.addOverride(1234L, "com.some.package", false);
+
+        assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create()
+                .withPackageName("com.some.package").build())).isFalse();
+        assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create()
+                .withPackageName("com.other.package").build())).isTrue();
     }
 
     @Test
-    public void testPackageOverrideUnknownChange() {
-        CompatConfig pc = new CompatConfig();
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isTrue();
+    public void testPackageOverrideUnknownPackage() throws Exception {
+        CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+
+        compatConfig.addOverride(1234L, "com.some.package", false);
+
+        assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create()
+                .withPackageName("com.some.package").build())).isFalse();
+        assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create()
+                .withPackageName("com.other.package").build())).isTrue();
     }
 
     @Test
-    public void testRemovePackageOverride() {
-        CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
-        pc.addOverride(1234L, "com.some.package", false);
-        pc.removeOverride(1234L, "com.some.package");
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isTrue();
+    public void testPreventAddOverride() throws Exception {
+        final long changeId = 1234L;
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1234L)
+                .build();
+        ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+                .withPackageName("com.some.package")
+                .build();
+        PackageManager packageManager = mock(PackageManager.class);
+        when(mContext.getPackageManager()).thenReturn(packageManager);
+        when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+            .thenReturn(applicationInfo);
+
+        // Force the validator to prevent overriding the change by using a user build.
+        when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+        when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+        assertThrows(SecurityException.class,
+                () -> compatConfig.addOverride(1234L, "com.some.package", true)
+        );
+        assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
     }
 
     @Test
-    public void testLookupChangeId() {
-        CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
-        pc.addChange(new CompatChange(2345L, "ANOTHER_CHANGE", -1, false, null));
-        assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(1234L);
+    public void testPreventRemoveOverride() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1234L)
+                .build();
+        ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+                .withPackageName("com.some.package")
+                .build();
+        when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+            .thenReturn(applicationInfo);
+        // Assume the override was allowed to be added.
+        compatConfig.addOverride(1234L, "com.some.package", true);
+
+        // Validator allows turning on the change.
+        assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
+
+        // Reject all override attempts.
+        // Force the validator to prevent overriding the change by using a user build.
+        when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+        when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+        // Try to turn off change, but validator prevents it.
+        assertThrows(SecurityException.class,
+                () -> compatConfig.removeOverride(1234L, "com.some.package"));
+        assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
     }
 
     @Test
-    public void testLookupChangeIdNotPresent() {
-        CompatConfig pc = new CompatConfig();
-        assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(-1L);
+    public void testRemovePackageOverride() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addEnabledChangeWithId(1234L)
+                .build();
+        ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+                .withPackageName("com.some.package")
+                .build();
+
+        assertThat(compatConfig.addOverride(1234L, "com.some.package", false)).isTrue();
+        assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
+
+        compatConfig.removeOverride(1234L, "com.some.package");
+        assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
+    }
+
+    @Test
+    public void testLookupChangeId() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addEnabledChangeWithIdAndName(1234L, "MY_CHANGE")
+                .addEnabledChangeWithIdAndName(2345L, "MY_OTHER_CHANGE")
+                .build();
+
+        assertThat(compatConfig.lookupChangeId("MY_CHANGE")).isEqualTo(1234L);
+    }
+
+    @Test
+    public void testLookupChangeIdNotPresent() throws Exception {
+        CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+        assertThat(compatConfig.lookupChangeId("MY_CHANGE")).isEqualTo(-1L);
     }
 
     @Test
@@ -172,14 +279,17 @@
 
         File dir = createTempDir();
         writeToFile(dir, "platform_compat_config.xml", configXml);
+        CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+        compatConfig.initConfigFromLib(dir);
 
-        CompatConfig pc = new CompatConfig();
-        pc.initConfigFromLib(dir);
-
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
-        assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse();
-        assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue();
+        assertThat(compatConfig.isChangeEnabled(1234L,
+            ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse();
+        assertThat(compatConfig.isChangeEnabled(1234L,
+            ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue();
+        assertThat(compatConfig.isChangeEnabled(1235L,
+            ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse();
+        assertThat(compatConfig.isChangeEnabled(1236L,
+            ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
     }
 
     @Test
@@ -195,15 +305,16 @@
         File dir = createTempDir();
         writeToFile(dir, "libcore_platform_compat_config.xml", configXml1);
         writeToFile(dir, "frameworks_platform_compat_config.xml", configXml2);
+        CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+        compatConfig.initConfigFromLib(dir);
 
-        CompatConfig pc = new CompatConfig();
-        pc.initConfigFromLib(dir);
-
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
-        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
-        assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse();
-        assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue();
+        assertThat(compatConfig.isChangeEnabled(1234L,
+            ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse();
+        assertThat(compatConfig.isChangeEnabled(1234L,
+            ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue();
+        assertThat(compatConfig.isChangeEnabled(1235L,
+            ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse();
+        assertThat(compatConfig.isChangeEnabled(1236L,
+            ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
     }
 }
-
-
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatibilityChangeConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatibilityChangeConfigBuilder.java
new file mode 100644
index 0000000..793296e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatibilityChangeConfigBuilder.java
@@ -0,0 +1,52 @@
+/*
+ * 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.server.compat;
+
+import android.compat.Compatibility;
+
+import com.android.internal.compat.CompatibilityChangeConfig;
+
+import java.util.HashSet;
+import java.util.Set;
+
+class CompatibilityChangeConfigBuilder {
+    private Set<Long> mEnabled;
+    private Set<Long> mDisabled;
+
+    private CompatibilityChangeConfigBuilder() {
+        mEnabled = new HashSet<>();
+        mDisabled = new HashSet<>();
+    }
+
+    static CompatibilityChangeConfigBuilder create() {
+        return new CompatibilityChangeConfigBuilder();
+    }
+
+    CompatibilityChangeConfigBuilder enable(Long id) {
+        mEnabled.add(id);
+        return this;
+    }
+
+    CompatibilityChangeConfigBuilder disable(Long id) {
+        mDisabled.add(id);
+        return this;
+    }
+
+    CompatibilityChangeConfig build() {
+        return new CompatibilityChangeConfig(new Compatibility.ChangeConfig(mEnabled, mDisabled));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
new file mode 100644
index 0000000..ecd07bd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
@@ -0,0 +1,383 @@
+/*
+ * 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.server.compat;
+
+import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
+import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
+import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
+import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.compat.AndroidBuildClassifier;
+import com.android.internal.compat.IOverrideValidator;
+import com.android.internal.compat.OverrideAllowedState;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class OverrideValidatorImplTest {
+    private static final String PACKAGE_NAME = "my.package";
+    private static final int TARGET_SDK = 10;
+    private static final int TARGET_SDK_BEFORE = 9;
+    private static final int TARGET_SDK_AFTER = 11;
+
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    Context mContext;
+
+    private AndroidBuildClassifier debuggableBuild() {
+        AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
+        when(buildClassifier.isDebuggableBuild()).thenReturn(true);
+        return buildClassifier;
+    }
+
+    private AndroidBuildClassifier betaBuild() {
+        AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
+        when(buildClassifier.isDebuggableBuild()).thenReturn(false);
+        when(buildClassifier.isFinalBuild()).thenReturn(false);
+        return buildClassifier;
+    }
+
+    private AndroidBuildClassifier finalBuild() {
+        AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
+        when(buildClassifier.isDebuggableBuild()).thenReturn(false);
+        when(buildClassifier.isFinalBuild()).thenReturn(true);
+        return buildClassifier;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+    }
+
+    @Test
+    public void getOverrideAllowedState_debugBuildAnyChangeDebugApp_allowOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+                    .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                    .addTargetSdkChangeWithId(TARGET_SDK, 2)
+                    .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                    .addEnabledChangeWithId(4)
+                    .addDisabledChangeWithId(5).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .debuggable()
+                        .withTargetSdk(TARGET_SDK)
+                        .withPackageName(PACKAGE_NAME).build());
+
+        OverrideAllowedState stateTargetSdkLessChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkEqualChange =
+                overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkAfterChange =
+                overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+        OverrideAllowedState stateEnabledChange =
+                overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+        OverrideAllowedState stateDisabledChange =
+                overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+
+        assertThat(stateTargetSdkLessChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+        assertThat(stateTargetSdkEqualChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+        assertThat(stateTargetSdkAfterChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+        assertThat(stateEnabledChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+        assertThat(stateDisabledChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+    }
+
+    @Test
+    public void getOverrideAllowedState_debugBuildAnyChangeReleaseApp_allowOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+                    .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                    .addTargetSdkChangeWithId(TARGET_SDK, 2)
+                    .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                    .addEnabledChangeWithId(4)
+                    .addDisabledChangeWithId(5).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .withTargetSdk(TARGET_SDK).build());
+
+        OverrideAllowedState stateTargetSdkLessChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkEqualChange =
+                overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkAfterChange =
+                overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+        OverrideAllowedState stateEnabledChange =
+                overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+        OverrideAllowedState stateDisabledChange =
+                overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+
+        assertThat(stateTargetSdkLessChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+        assertThat(stateTargetSdkEqualChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+        assertThat(stateTargetSdkAfterChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+        assertThat(stateEnabledChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+        assertThat(stateDisabledChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+    }
+
+    @Test
+    public void getOverrideAllowedState_betaBuildTargetSdkChangeDebugApp_allowOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext)
+                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                        .addTargetSdkChangeWithId(TARGET_SDK, 2)
+                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .debuggable()
+                        .withTargetSdk(TARGET_SDK)
+                        .withPackageName(PACKAGE_NAME).build());
+
+        OverrideAllowedState stateTargetSdkLessChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkEqualChange =
+                overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkAfterChange =
+                overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+
+        assertThat(stateTargetSdkLessChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_BEFORE));
+        assertThat(stateTargetSdkEqualChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK));
+        assertThat(stateTargetSdkAfterChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_AFTER));
+    }
+
+    @Test
+    public void getOverrideAllowedState_betaBuildEnabledChangeDebugApp_rejectOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext)
+                        .addEnabledChangeWithId(1).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .debuggable()
+                        .build());
+
+        OverrideAllowedState allowedState =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+        assertThat(allowedState)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1));
+    }
+
+    @Test
+    public void getOverrideAllowedState_betaBuildDisabledChangeDebugApp_rejectOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext)
+                        .addDisabledChangeWithId(1).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .debuggable()
+                        .withPackageName(PACKAGE_NAME).build());
+
+        OverrideAllowedState allowedState =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+        assertThat(allowedState)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1));
+    }
+
+    @Test
+    public void getOverrideAllowedState_betaBuildAnyChangeReleaseApp_rejectOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext)
+                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                        .addTargetSdkChangeWithId(TARGET_SDK, 2)
+                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                        .addEnabledChangeWithId(4)
+                        .addDisabledChangeWithId(5).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .withTargetSdk(TARGET_SDK).build());
+
+        OverrideAllowedState stateTargetSdkLessChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkEqualChange =
+                overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkAfterChange =
+                overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+        OverrideAllowedState stateEnabledChange =
+                overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+        OverrideAllowedState stateDisabledChange =
+                overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+
+        assertThat(stateTargetSdkLessChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+        assertThat(stateTargetSdkEqualChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+        assertThat(stateTargetSdkAfterChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+        assertThat(stateEnabledChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+        assertThat(stateDisabledChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+    }
+
+    @Test
+    public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptin_allowOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 1).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .debuggable()
+                        .withTargetSdk(TARGET_SDK)
+                        .withPackageName(PACKAGE_NAME).build());
+
+        OverrideAllowedState allowedState =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+        assertThat(allowedState)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_AFTER));
+    }
+
+    @Test
+    public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptout_rejectOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                        .addTargetSdkChangeWithId(TARGET_SDK, 2).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .withTargetSdk(TARGET_SDK)
+                        .debuggable()
+                        .build());
+
+        OverrideAllowedState stateTargetSdkLessChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkEqualChange =
+                overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+
+        assertThat(stateTargetSdkLessChange).isEqualTo(
+                new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, TARGET_SDK,
+                                         TARGET_SDK_BEFORE));
+        assertThat(stateTargetSdkEqualChange).isEqualTo(
+                new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, TARGET_SDK, TARGET_SDK));
+    }
+
+    @Test
+    public void getOverrideAllowedState_finalBuildEnabledChangeDebugApp_rejectOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+                        .addEnabledChangeWithId(1).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .debuggable().build());
+
+        OverrideAllowedState allowedState =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+        assertThat(allowedState)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1));
+    }
+
+    @Test
+    public void getOverrideAllowedState_finalBuildDisabledChangeDebugApp_rejectOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+                        .addDisabledChangeWithId(1).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .debuggable().build());
+
+        OverrideAllowedState allowedState =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+        assertThat(allowedState)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1));
+    }
+
+    @Test
+    public void getOverrideAllowedState_finalBuildAnyChangeReleaseApp_rejectOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                        .addTargetSdkChangeWithId(TARGET_SDK, 2)
+                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                        .addEnabledChangeWithId(4)
+                        .addDisabledChangeWithId(5).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .withTargetSdk(TARGET_SDK).build());
+
+        OverrideAllowedState stateTargetSdkLessChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkEqualChange =
+                overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkAfterChange =
+                overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+        OverrideAllowedState stateEnabledChange =
+                overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+        OverrideAllowedState stateDisabledChange =
+                overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+
+        assertThat(stateTargetSdkLessChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+        assertThat(stateTargetSdkEqualChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+        assertThat(stateTargetSdkAfterChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+        assertThat(stateEnabledChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+        assertThat(stateDisabledChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index c406876..ce5d6d9 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -26,21 +26,20 @@
 import static org.mockito.internal.verification.VerificationModeFactory.times;
 import static org.testng.Assert.assertThrows;
 
-import android.compat.Compatibility;
 import android.content.Context;
 import android.content.pm.PackageManager;
 
-import com.android.internal.compat.CompatibilityChangeConfig;
+import androidx.test.runner.AndroidJUnit4;
 
-import com.google.common.collect.ImmutableSet;
+import com.android.internal.compat.AndroidBuildClassifier;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.MockitoAnnotations;
 
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class PlatformCompatTest {
     private static final String PACKAGE_NAME = "my.package";
 
@@ -50,84 +49,77 @@
     private PackageManager mPackageManager;
     @Mock
     CompatChange.ChangeListener mListener1, mListener2;
-
+    PlatformCompat mPlatformCompat;
+    CompatConfig mCompatConfig;
+    @Mock
+    private AndroidBuildClassifier mBuildClassifier;
 
     @Before
     public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mPackageManager.getPackageUid(eq(PACKAGE_NAME), eq(0))).thenThrow(
                 new PackageManager.NameNotFoundException());
-        CompatConfig.get().clearChanges();
+        mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        // Assume userdebug/eng non-final build
+        when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
+        when(mBuildClassifier.isFinalBuild()).thenReturn(false);
     }
 
     @Test
-    public void testRegisterListenerToSameIdThrows() {
-        PlatformCompat pc = new PlatformCompat(mContext);
-
+    public void testRegisterListenerToSameIdThrows() throws Exception {
         // Registering a listener to change 1 is successful.
-        pc.registerListener(1, mListener1);
+        mPlatformCompat.registerListener(1, mListener1);
         // Registering a listener to change 2 is successful.
-        pc.registerListener(2, mListener1);
+        mPlatformCompat.registerListener(2, mListener1);
         // Trying to register another listener to change id 1 fails.
-        assertThrows(IllegalStateException.class, () -> pc.registerListener(1, mListener1));
+        assertThrows(IllegalStateException.class,
+                () -> mPlatformCompat.registerListener(1, mListener1));
     }
 
     @Test
-    public void testRegisterListenerReturn() {
-        PlatformCompat pc = new PlatformCompat(mContext);
-
-        pc.setOverrides(
-                new CompatibilityChangeConfig(
-                        new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of())),
+    public void testRegisterListenerReturn() throws Exception {
+        mPlatformCompat.setOverrides(
+                CompatibilityChangeConfigBuilder.create().enable(1L).build(),
                 PACKAGE_NAME);
 
         // Change id 1 is known (added in setOverrides).
-        assertThat(pc.registerListener(1, mListener1)).isTrue();
+        assertThat(mPlatformCompat.registerListener(1, mListener1)).isTrue();
         // Change 2 is unknown.
-        assertThat(pc.registerListener(2, mListener1)).isFalse();
+        assertThat(mPlatformCompat.registerListener(2, mListener1)).isFalse();
     }
 
     @Test
-    public void testListenerCalledOnSetOverrides() {
-        PlatformCompat pc = new PlatformCompat(mContext);
+    public void testListenerCalledOnSetOverrides() throws Exception {
+        mPlatformCompat.registerListener(1, mListener1);
+        mPlatformCompat.registerListener(2, mListener1);
 
-        pc.registerListener(1, mListener1);
-        pc.registerListener(2, mListener1);
-
-        pc.setOverrides(
-                new CompatibilityChangeConfig(
-                        new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))),
+        mPlatformCompat.setOverrides(
+                CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
                 PACKAGE_NAME);
 
         verify(mListener1, times(2)).onCompatChange(PACKAGE_NAME);
     }
 
     @Test
-    public void testListenerNotCalledOnWrongPackage() {
-        PlatformCompat pc = new PlatformCompat(mContext);
+    public void testListenerNotCalledOnWrongPackage() throws Exception {
+        mPlatformCompat.registerListener(1, mListener1);
+        mPlatformCompat.registerListener(2, mListener1);
 
-        pc.registerListener(1, mListener1);
-        pc.registerListener(2, mListener1);
-
-        pc.setOverridesForTest(
-                new CompatibilityChangeConfig(
-                        new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))),
+        mPlatformCompat.setOverrides(
+                CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
                 PACKAGE_NAME);
 
         verify(mListener1, never()).onCompatChange("other.package");
     }
 
     @Test
-    public void testListenerCalledOnSetOverridesTwoListeners() {
-        PlatformCompat pc = new PlatformCompat(mContext);
-        pc.registerListener(1, mListener1);
+    public void testListenerCalledOnSetOverridesTwoListeners() throws Exception {
+        mPlatformCompat.registerListener(1, mListener1);
 
-        final ImmutableSet<Long> enabled = ImmutableSet.of(1L);
-        final ImmutableSet<Long> disabled = ImmutableSet.of(2L);
-
-        pc.setOverrides(
-                new CompatibilityChangeConfig(
-                        new Compatibility.ChangeConfig(enabled, disabled)),
+        mPlatformCompat.setOverrides(
+                CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
                 PACKAGE_NAME);
 
         verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
@@ -136,11 +128,10 @@
         reset(mListener1);
         reset(mListener2);
 
-        pc.registerListener(2, mListener2);
+        mPlatformCompat.registerListener(2, mListener2);
 
-        pc.setOverrides(
-                new CompatibilityChangeConfig(
-                        new Compatibility.ChangeConfig(enabled, disabled)),
+        mPlatformCompat.setOverrides(
+                CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
                 PACKAGE_NAME);
 
         verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
@@ -148,31 +139,23 @@
     }
 
     @Test
-    public void testListenerCalledOnSetOverridesForTest() {
-        PlatformCompat pc = new PlatformCompat(mContext);
+    public void testListenerCalledOnSetOverridesForTest() throws Exception {
+        mPlatformCompat.registerListener(1, mListener1);
+        mPlatformCompat.registerListener(2, mListener1);
 
-        pc.registerListener(1, mListener1);
-        pc.registerListener(2, mListener1);
-
-        pc.setOverridesForTest(
-                new CompatibilityChangeConfig(
-                        new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))),
+        mPlatformCompat.setOverrides(
+                CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
                 PACKAGE_NAME);
 
         verify(mListener1, times(2)).onCompatChange(PACKAGE_NAME);
     }
 
     @Test
-    public void testListenerCalledOnSetOverridesTwoListenersForTest() {
-        PlatformCompat pc = new PlatformCompat(mContext);
-        pc.registerListener(1, mListener1);
+    public void testListenerCalledOnSetOverridesTwoListenersForTest() throws Exception {
+        mPlatformCompat.registerListener(1, mListener1);
 
-        final ImmutableSet<Long> enabled = ImmutableSet.of(1L);
-        final ImmutableSet<Long> disabled = ImmutableSet.of(2L);
-
-        pc.setOverridesForTest(
-                new CompatibilityChangeConfig(
-                        new Compatibility.ChangeConfig(enabled, disabled)),
+        mPlatformCompat.setOverrides(
+                CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
                 PACKAGE_NAME);
 
         verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
@@ -181,10 +164,10 @@
         reset(mListener1);
         reset(mListener2);
 
-        pc.registerListener(2, mListener2);
-        pc.setOverridesForTest(
-                new CompatibilityChangeConfig(
-                        new Compatibility.ChangeConfig(enabled, disabled)),
+        mPlatformCompat.registerListener(2, mListener2);
+
+        mPlatformCompat.setOverrides(
+                CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
                 PACKAGE_NAME);
 
         verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
@@ -192,15 +175,12 @@
     }
 
     @Test
-    public void testListenerCalledOnClearOverrides() {
-        PlatformCompat pc = new PlatformCompat(mContext);
+    public void testListenerCalledOnClearOverrides() throws Exception {
+        mPlatformCompat.registerListener(1, mListener1);
+        mPlatformCompat.registerListener(2, mListener2);
 
-        pc.registerListener(1, mListener1);
-        pc.registerListener(2, mListener2);
-
-        pc.setOverrides(
-                new CompatibilityChangeConfig(
-                        new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of())),
+        mPlatformCompat.setOverrides(
+                CompatibilityChangeConfigBuilder.create().enable(1L).build(),
                 PACKAGE_NAME);
         verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
         verify(mListener2, never()).onCompatChange(PACKAGE_NAME);
@@ -208,21 +188,18 @@
         reset(mListener1);
         reset(mListener2);
 
-        pc.clearOverrides(PACKAGE_NAME);
+        mPlatformCompat.clearOverrides(PACKAGE_NAME);
         verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
         verify(mListener2, never()).onCompatChange(PACKAGE_NAME);
     }
 
     @Test
-    public void testListenerCalledOnClearOverridesMultipleOverrides() {
-        PlatformCompat pc = new PlatformCompat(mContext);
+    public void testListenerCalledOnClearOverridesMultipleOverrides() throws Exception {
+        mPlatformCompat.registerListener(1, mListener1);
+        mPlatformCompat.registerListener(2, mListener2);
 
-        pc.registerListener(1, mListener1);
-        pc.registerListener(2, mListener2);
-
-        pc.setOverrides(
-                new CompatibilityChangeConfig(
-                        new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))),
+        mPlatformCompat.setOverrides(
+                CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
                 PACKAGE_NAME);
         verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
         verify(mListener2, times(1)).onCompatChange(PACKAGE_NAME);
@@ -230,21 +207,18 @@
         reset(mListener1);
         reset(mListener2);
 
-        pc.clearOverrides(PACKAGE_NAME);
+        mPlatformCompat.clearOverrides(PACKAGE_NAME);
         verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
         verify(mListener2, times(1)).onCompatChange(PACKAGE_NAME);
     }
 
     @Test
-    public void testListenerCalledOnClearOverrideExists() {
-        PlatformCompat pc = new PlatformCompat(mContext);
+    public void testListenerCalledOnClearOverrideExists() throws Exception {
+        mPlatformCompat.registerListener(1, mListener1);
+        mPlatformCompat.registerListener(2, mListener2);
 
-        pc.registerListener(1, mListener1);
-        pc.registerListener(2, mListener2);
-
-        pc.setOverrides(
-                new CompatibilityChangeConfig(
-                        new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of())),
+        mPlatformCompat.setOverrides(
+                CompatibilityChangeConfigBuilder.create().enable(1L).build(),
                 PACKAGE_NAME);
         verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
         verify(mListener2, never()).onCompatChange(PACKAGE_NAME);
@@ -252,21 +226,17 @@
         reset(mListener1);
         reset(mListener2);
 
-        pc.clearOverride(1, PACKAGE_NAME);
+        mPlatformCompat.clearOverride(1, PACKAGE_NAME);
         verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
         verify(mListener2, never()).onCompatChange(PACKAGE_NAME);
     }
 
     @Test
-    public void testListenerCalledOnClearOverrideDoesntExist() {
-        PlatformCompat pc = new PlatformCompat(mContext);
+    public void testListenerCalledOnClearOverrideDoesntExist() throws Exception {
+        mPlatformCompat.registerListener(1, mListener1);
 
-        pc.registerListener(1, mListener1);
-
-        pc.clearOverride(1, PACKAGE_NAME);
+        mPlatformCompat.clearOverride(1, PACKAGE_NAME);
         // Listener not called when a non existing override is removed.
         verify(mListener1, never()).onCompatChange(PACKAGE_NAME);
     }
-
-
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index a16e14f..175c756 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3636,6 +3636,29 @@
         verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0);
     }
 
+    public void testIsOrganizationOwnedDevice() throws Exception {
+        setupProfileOwner();
+        // Set up the user manager to return correct user info
+        UserInfo managedProfileUserInfo = new UserInfo(DpmMockContext.CALLER_USER_HANDLE,
+                "managed profile",
+                UserInfo.FLAG_MANAGED_PROFILE);
+        when(getServices().userManager.getUsers())
+                .thenReturn(Arrays.asList(managedProfileUserInfo));
+
+        // Any caller without the MANAGE_USERS permission should get a security exception.
+        assertExpectException(SecurityException.class, null, () ->
+                dpm.isOrganizationOwnedDeviceWithManagedProfile());
+        // But when the right permission is granted, this should succeed.
+        mContext.permissions.add(android.Manifest.permission.MANAGE_USERS);
+        assertFalse(dpm.isOrganizationOwnedDeviceWithManagedProfile());
+        configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+        assertTrue(dpm.isOrganizationOwnedDeviceWithManagedProfile());
+
+        // A random caller from another user should also be able to get the right result.
+        mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
+        assertTrue(dpm.isOrganizationOwnedDeviceWithManagedProfile());
+    }
+
     public void testSetTime() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index ebca240..25d0778 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -78,7 +78,7 @@
         int displayId = 0;
 
         // With no votes present, DisplayModeDirector should allow any refresh rate.
-        assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*defaultModeId=*/60,
+        assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*baseModeId=*/60,
                              new DisplayModeDirector.RefreshRateRange(0f, Float.POSITIVE_INFINITY)),
                 createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayModeSpecs(
                         displayId));
@@ -105,7 +105,7 @@
                 director.injectVotesByDisplay(votesByDisplay);
                 assertEquals(
                         new DisplayModeDirector.DesiredDisplayModeSpecs(
-                                /*defaultModeId=*/minFps + i,
+                                /*baseModeId=*/minFps + i,
                                 new DisplayModeDirector.RefreshRateRange(minFps + i, maxFps - i)),
                         director.getDesiredDisplayModeSpecs(displayId));
             }
@@ -126,7 +126,7 @@
             votes.put(DisplayModeDirector.Vote.MIN_PRIORITY,
                     DisplayModeDirector.Vote.forRefreshRates(70, 80));
             director.injectVotesByDisplay(votesByDisplay);
-            assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*defaultModeId=*/70,
+            assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*baseModeId=*/70,
                                  new DisplayModeDirector.RefreshRateRange(70, 80)),
                     director.getDesiredDisplayModeSpecs(displayId));
         }
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
new file mode 100644
index 0000000..742952e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.server.integrity.parser;
+
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
+import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
+import static com.android.server.integrity.utils.TestUtils.getBits;
+import static com.android.server.integrity.utils.TestUtils.getBytes;
+import static com.android.server.integrity.utils.TestUtils.getValueBits;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.integrity.AppInstallMetadata;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class RuleIndexingControllerTest {
+
+    @Test
+    public void verifyIndexRangeSearchIsCorrect() throws IOException {
+        InputStream inputStream = obtainDefaultIndexingMapForTest();
+
+        RuleIndexingController indexingController = new RuleIndexingController(inputStream);
+
+        AppInstallMetadata appInstallMetadata =
+                new AppInstallMetadata.Builder()
+                        .setPackageName("ddd")
+                        .setAppCertificate("777")
+                        .build();
+
+        List<RuleIndexRange> resultingIndexes =
+                indexingController.identifyRulesToEvaluate(appInstallMetadata);
+
+        assertThat(resultingIndexes)
+                .containsExactly(
+                        new RuleIndexRange(200, 300),
+                        new RuleIndexRange(700, 800),
+                        new RuleIndexRange(900, 945));
+    }
+
+    @Test
+    public void verifyIndexRangeSearchIsCorrect_keysInFirstAndLastBlock() throws IOException {
+        InputStream inputStream = obtainDefaultIndexingMapForTest();
+
+        RuleIndexingController indexingController = new RuleIndexingController(inputStream);
+
+        AppInstallMetadata appInstallMetadata =
+                new AppInstallMetadata.Builder()
+                        .setPackageName("bbb")
+                        .setAppCertificate("999")
+                        .build();
+
+        List<RuleIndexRange> resultingIndexes =
+                indexingController.identifyRulesToEvaluate(appInstallMetadata);
+
+        assertThat(resultingIndexes)
+                .containsExactly(
+                        new RuleIndexRange(100, 200),
+                        new RuleIndexRange(800, 900),
+                        new RuleIndexRange(900, 945));
+    }
+
+    @Test
+    public void verifyIndexRangeSearchIsCorrect_keysMatchWithValues() throws IOException {
+        InputStream inputStream = obtainDefaultIndexingMapForTest();
+
+        RuleIndexingController indexingController = new RuleIndexingController(inputStream);
+
+        AppInstallMetadata appInstallMetadata =
+                new AppInstallMetadata.Builder()
+                        .setPackageName("ccc")
+                        .setAppCertificate("444")
+                        .build();
+
+        List<RuleIndexRange> resultingIndexes =
+                indexingController.identifyRulesToEvaluate(appInstallMetadata);
+
+        assertThat(resultingIndexes)
+                .containsExactly(
+                        new RuleIndexRange(200, 300),
+                        new RuleIndexRange(700, 800),
+                        new RuleIndexRange(900, 945));
+    }
+
+    @Test
+    public void verifyIndexRangeSearchIsCorrect_noIndexesAvailable() throws IOException {
+        byte[] stringBytes =
+                getBytes(
+                        getKeyValueString(START_INDEXING_KEY, 100)
+                                + getKeyValueString(END_INDEXING_KEY, 500)
+                                + getKeyValueString(START_INDEXING_KEY, 500)
+                                + getKeyValueString(END_INDEXING_KEY, 900)
+                                + getKeyValueString(START_INDEXING_KEY, 900)
+                                + getKeyValueString(END_INDEXING_KEY, 945));
+        ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
+        rule.put(stringBytes);
+        InputStream inputStream = new ByteArrayInputStream(rule.array());
+
+        RuleIndexingController indexingController = new RuleIndexingController(inputStream);
+
+        AppInstallMetadata appInstallMetadata =
+                new AppInstallMetadata.Builder()
+                        .setPackageName("ccc")
+                        .setAppCertificate("444")
+                        .build();
+
+        List<RuleIndexRange> resultingIndexes =
+                indexingController.identifyRulesToEvaluate(appInstallMetadata);
+
+        assertThat(resultingIndexes)
+                .containsExactly(
+                        new RuleIndexRange(100, 500),
+                        new RuleIndexRange(500, 900),
+                        new RuleIndexRange(900, 945));
+    }
+
+    private static InputStream obtainDefaultIndexingMapForTest() {
+        byte[] stringBytes =
+                getBytes(
+                        getKeyValueString(START_INDEXING_KEY, 100)
+                                + getKeyValueString("ccc", 200)
+                                + getKeyValueString("eee", 300)
+                                + getKeyValueString("hhh", 400)
+                                + getKeyValueString(END_INDEXING_KEY, 500)
+                                + getKeyValueString(START_INDEXING_KEY, 500)
+                                + getKeyValueString("111", 600)
+                                + getKeyValueString("444", 700)
+                                + getKeyValueString("888", 800)
+                                + getKeyValueString(END_INDEXING_KEY, 900)
+                                + getKeyValueString(START_INDEXING_KEY, 900)
+                                + getKeyValueString(END_INDEXING_KEY, 945));
+        ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
+        rule.put(stringBytes);
+        return new ByteArrayInputStream(rule.array());
+    }
+
+    private static String getKeyValueString(String key, int value) {
+        String isNotHashed = "0";
+        return isNotHashed
+                + getBits(key.length(), VALUE_SIZE_BITS)
+                + getValueBits(key)
+                + getBits(value, /* numOfBits= */ 32);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 28e6830..c7dbad8 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1273,11 +1273,11 @@
             history.recordData(start, end,
                     new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
             stats.clear();
-            stats.addValues(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
+            stats.addEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
-            stats.addValues(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
+            stats.addEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
-            stats.addValues(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
+            stats.addEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
 
             reset(mNotifManager);
@@ -1301,9 +1301,9 @@
             history.recordData(start, end,
                     new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
             stats.clear();
-            stats.addValues(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
+            stats.addEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(960), 0, 0, 0, 0);
-            stats.addValues(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
+            stats.addEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
 
             reset(mNotifManager);
diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
new file mode 100644
index 0000000..7666ab9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
@@ -0,0 +1,287 @@
+/*
+ * 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.server.power;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.SensorManager;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ServiceManager;
+import android.os.Vibrator;
+import android.os.test.TestLooper;
+import android.provider.Settings;
+import android.testing.TestableContext;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.LocalServices;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.power.batterysaver.BatterySaverPolicy;
+import com.android.server.power.batterysaver.BatterySavingStats;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link com.android.server.power.Notifier}
+ */
+public class NotifierTest {
+    private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
+    private static final int USER_ID = 0;
+
+    @Mock private BatterySaverPolicy mBatterySaverPolicyMock;
+    @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
+    @Mock private Notifier mNotifierMock;
+    @Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
+    @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
+    @Mock private SystemPropertiesWrapper mSystemPropertiesMock;
+    @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
+    @Mock private BatteryStatsImpl mBatteryStats;
+    @Mock private Vibrator mVibrator;
+    @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+
+    private PowerManagerService mService;
+    private Context mContextSpy;
+    private Resources mResourcesSpy;
+    private TestLooper mTestLooper = new TestLooper();
+    private Notifier mNotifier;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+        LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
+
+        mContextSpy = spy(new TestableContext(InstrumentationRegistry.getContext()));
+        mResourcesSpy = spy(mContextSpy.getResources());
+        when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
+        when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
+        when(mContextSpy.getSystemService(Vibrator.class)).thenReturn(mVibrator);
+
+        mService = new PowerManagerService(mContextSpy, mInjector);
+    }
+
+    @Test
+    public void testVibrateEnabled_wiredCharging() {
+        createNotifier();
+
+        // GIVEN the charging vibration is enabled
+        enableChargingVibration(true);
+
+        // WHEN wired charging starts
+        mNotifier.onWiredChargingStarted(USER_ID);
+        mTestLooper.dispatchAll();
+
+        // THEN the device vibrates once
+        verify(mVibrator, times(1)).vibrate(any(), any());
+    }
+
+    @Test
+    public void testVibrateDisabled_wiredCharging() {
+        createNotifier();
+
+        // GIVEN the charging vibration is disabled
+        enableChargingVibration(false);
+
+        // WHEN wired charging starts
+        mNotifier.onWiredChargingStarted(USER_ID);
+        mTestLooper.dispatchAll();
+
+        // THEN the device doesn't vibrate
+        verify(mVibrator, never()).vibrate(any(), any());
+    }
+
+    @Test
+    public void testVibrateEnabled_wirelessCharging() {
+        createNotifier();
+
+        // GIVEN the charging vibration is enabled
+        enableChargingVibration(true);
+
+        // WHEN wireless charging starts
+        mNotifier.onWirelessChargingStarted(5, USER_ID);
+        mTestLooper.dispatchAll();
+
+        // THEN the device vibrates once
+        verify(mVibrator, times(1)).vibrate(any(), any());
+    }
+
+    @Test
+    public void testVibrateDisabled_wirelessCharging() {
+        createNotifier();
+
+        // GIVEN the charging vibration is disabeld
+        enableChargingVibration(false);
+
+        // WHEN wireless charging starts
+        mNotifier.onWirelessChargingStarted(5, USER_ID);
+        mTestLooper.dispatchAll();
+
+        // THEN the device doesn't vibrate
+        verify(mVibrator, never()).vibrate(any(), any());
+    }
+
+    @Test
+    public void testVibrateEnabled_dndOn() {
+        createNotifier();
+
+        // GIVEN the charging vibration is enabled but dnd is on
+        enableChargingVibration(true);
+        enableChargingFeedback(
+                /* chargingFeedbackEnabled */ true,
+                /* dndOn */ true);
+
+        // WHEN wired charging starts
+        mNotifier.onWiredChargingStarted(USER_ID);
+        mTestLooper.dispatchAll();
+
+        // THEN the device doesn't vibrate
+        verify(mVibrator, never()).vibrate(any(), any());
+    }
+
+    @Test
+    public void testWirelessAnimationEnabled() {
+        // GIVEN the wireless charging animation is enabled
+        when(mResourcesSpy.getBoolean(
+                com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim))
+                .thenReturn(true);
+        createNotifier();
+
+        // WHEN wireless charging starts
+        mNotifier.onWirelessChargingStarted(5, USER_ID);
+        mTestLooper.dispatchAll();
+
+        // THEN the charging animation is triggered
+        verify(mStatusBarManagerInternal, times(1)).showChargingAnimation(5);
+    }
+
+    @Test
+    public void testWirelessAnimationDisabled() {
+        // GIVEN the wireless charging animation is disabled
+        when(mResourcesSpy.getBoolean(
+                com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim))
+                .thenReturn(false);
+        createNotifier();
+
+        // WHEN wireless charging starts
+        mNotifier.onWirelessChargingStarted(5, USER_ID);
+        mTestLooper.dispatchAll();
+
+        // THEN the charging animation never gets called
+        verify(mStatusBarManagerInternal, never()).showChargingAnimation(anyInt());
+    }
+
+    private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() {
+        @Override
+        Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+                SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+            return mNotifierMock;
+        }
+
+        @Override
+        SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+            return super.createSuspendBlocker(service, name);
+        }
+
+        @Override
+        BatterySaverPolicy createBatterySaverPolicy(
+                Object lock, Context context, BatterySavingStats batterySavingStats) {
+            return mBatterySaverPolicyMock;
+        }
+
+        @Override
+        PowerManagerService.NativeWrapper createNativeWrapper() {
+            return mNativeWrapperMock;
+        }
+
+        @Override
+        WirelessChargerDetector createWirelessChargerDetector(
+                SensorManager sensorManager, SuspendBlocker suspendBlocker, Handler handler) {
+            return mWirelessChargerDetectorMock;
+        }
+
+        @Override
+        AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) {
+            return mAmbientDisplayConfigurationMock;
+        }
+
+        @Override
+        InattentiveSleepWarningController createInattentiveSleepWarningController() {
+            return mInattentiveSleepWarningControllerMock;
+        }
+
+        @Override
+        public SystemPropertiesWrapper createSystemPropertiesWrapper() {
+            return mSystemPropertiesMock;
+        }
+    };
+
+    private void enableChargingFeedback(boolean chargingFeedbackEnabled, boolean dndOn) {
+        // enable/disable charging feedback
+        Settings.Secure.putIntForUser(
+                mContextSpy.getContentResolver(),
+                Settings.Secure.CHARGING_SOUNDS_ENABLED,
+                chargingFeedbackEnabled ? 1 : 0,
+                USER_ID);
+
+        // toggle on/off dnd
+        Settings.Global.putInt(
+                mContextSpy.getContentResolver(),
+                Settings.Global.ZEN_MODE,
+                dndOn ? Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                        : Settings.Global.ZEN_MODE_OFF);
+    }
+
+    private void enableChargingVibration(boolean enable) {
+        enableChargingFeedback(true, false);
+
+        Settings.Secure.putIntForUser(
+                mContextSpy.getContentResolver(),
+                Settings.Secure.CHARGING_VIBRATION_ENABLED,
+                enable ? 1 : 0,
+                USER_ID);
+    }
+
+    private void createNotifier() {
+        mNotifier = new Notifier(
+                mTestLooper.getLooper(),
+                mContextSpy,
+                IBatteryStats.Stub.asInterface(ServiceManager.getService(
+                        BatteryStats.SERVICE_NAME)),
+                mInjector.createSuspendBlocker(mService, "testBlocker"),
+                null);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 71b568c..ae53692 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -37,7 +37,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
 
 import androidx.test.runner.AndroidJUnit4;
 
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index ca6fd08..aaf9799 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -30,7 +30,7 @@
 import android.icu.util.Calendar;
 import android.icu.util.GregorianCalendar;
 import android.icu.util.TimeZone;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
 
 import androidx.test.runner.AndroidJUnit4;
 
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java
index 239d413..f1e9191 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
 
 import androidx.test.runner.AndroidJUnit4;
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 73b2f6b..a3e94599 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -521,11 +521,12 @@
     }
 
     @Test
-    public void testShouldPauseWhenMakeClientVisible() {
+    public void testShouldStartWhenMakeClientActive() {
         ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
         topActivity.setOccludesParent(false);
         mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
-        mActivity.makeClientVisible();
+        mActivity.setVisibility(true);
+        mActivity.makeActiveIfNeeded(null /* activeActivity */);
         assertEquals(STARTED, mActivity.getState());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
index 574517a..71390db 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
@@ -40,7 +40,7 @@
  * Tests for the {@link ActivityStack} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:AnimatingActivityRegistryTest
+ *  atest WmTests:AnimatingActivityRegistryTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 1311889..f6213bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -42,7 +42,7 @@
 
 /**
  * Build/Install/Run:
- *  atest FrameworksServicesTests:AppTransitionControllerTest
+ *  atest WmTests:AppTransitionControllerTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 0ad0f95..1dda535 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -60,7 +60,7 @@
  * appropriately.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:BoundsAnimationControllerTests
+ *  atest WmTests:BoundsAnimationControllerTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 0aa6961..e8f7849 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -40,7 +40,7 @@
 
 /**
  * Build/Install/Run:
- *  atest FrameworksServicesTests:DimmerTests
+ *  atest WmTests:DimmerTests
  */
 @Presubmit
 @RunWith(WindowTestRunner.class)
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 73dd2df..f2ba97c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -57,6 +57,8 @@
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -86,6 +88,7 @@
 import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.ViewRootImpl;
+import android.view.WindowManager;
 import android.view.test.InsetsModeSession;
 
 import androidx.test.filters.SmallTest;
@@ -561,8 +564,7 @@
         final DisplayContent dc = createNewDisplay();
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
 
-        dc.setLayoutNeeded();
-        dc.performLayout(true /* initial */, false /* updateImeWindows */);
+        performLayout(dc);
 
         assertThat(win.mLayoutSeq, is(dc.mLayoutSeq));
     }
@@ -829,8 +831,7 @@
         win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
         win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40)));
 
-        dc.setLayoutNeeded();
-        dc.performLayout(true /* initial */, false /* updateImeWindows */);
+        performLayout(dc);
 
         win.setHasSurface(true);
         dc.updateSystemGestureExclusion();
@@ -866,8 +867,7 @@
         win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
         win2.setSystemGestureExclusion(Collections.singletonList(new Rect(20, 30, 40, 50)));
 
-        dc.setLayoutNeeded();
-        dc.performLayout(true /* initial */, false /* updateImeWindows */);
+        performLayout(dc);
 
         win.setHasSurface(true);
         win2.setHasSurface(true);
@@ -898,8 +898,7 @@
         win2.getAttrs().height = 10;
         win2.setSystemGestureExclusion(Collections.emptyList());
 
-        dc.setLayoutNeeded();
-        dc.performLayout(true /* initial */, false /* updateImeWindows */);
+        performLayout(dc);
 
         win.setHasSurface(true);
         win2.setHasSurface(true);
@@ -922,8 +921,7 @@
                         | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
         win.mActivityRecord.mTargetSdk = P;
 
-        dc.setLayoutNeeded();
-        dc.performLayout(true /* initial */, false /* updateImeWindows */);
+        performLayout(dc);
 
         win.setHasSurface(true);
 
@@ -935,6 +933,22 @@
     }
 
     @Test
+    public void testRequestResizeForEmptyFrames() {
+        final WindowState win = mChildAppWindowAbove;
+        makeWindowVisible(win, win.getParentWindow());
+        win.setRequestedSize(mDisplayContent.mBaseDisplayWidth, 0 /* height */);
+        win.mAttrs.width = win.mAttrs.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        win.mAttrs.gravity = Gravity.CENTER;
+        performLayout(mDisplayContent);
+
+        // The frame is empty because the requested height is zero.
+        assertTrue(win.getFrameLw().isEmpty());
+        // The window should be scheduled to resize then the client may report a new non-empty size.
+        win.updateResizingWindowIfNeeded();
+        assertThat(mWm.mResizingWindows).contains(win);
+    }
+
+    @Test
     public void testOrientationChangeLogging() {
         MetricsLogger mockLogger = mock(MetricsLogger.class);
         Configuration oldConfig = new Configuration();
@@ -1011,6 +1025,11 @@
         mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
     }
 
+    private void performLayout(DisplayContent dc) {
+        dc.setLayoutNeeded();
+        dc.performLayout(true /* initial */, false /* updateImeWindows */);
+    }
+
     /**
      * Create DisplayContent that does not update display base/initial values from device to keep
      * the values set by test.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 112479b..1a57596 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -61,7 +61,7 @@
 
 /**
  * Build/Install/Run:
- *  atest FrameworksServicesTests:RemoteAnimationControllerTest
+ *  atest WmTests:RemoteAnimationControllerTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index e011280..bac2bca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -60,7 +60,7 @@
  * Test class for {@link SurfaceAnimationRunner}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:SurfaceAnimationRunnerTest
+ *  atest WmTests:SurfaceAnimationRunnerTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index 0274b7d..e712471 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -38,7 +38,7 @@
  * Test class for {@link TaskSnapshotCache}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:TaskSnapshotCacheTest
+ *  atest WmTests:TaskSnapshotCacheTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 8fe0cdb..2e485dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -53,7 +53,7 @@
  * Test class for {@link TaskSnapshotController}.
  *
  * Build/Install/Run:
- *  *  atest FrameworksServicesTests:TaskSnapshotControllerTest
+ *  *  atest WmTests:TaskSnapshotControllerTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index b5a5790..eb8eb98 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -47,7 +47,7 @@
  * Test class for {@link TaskSnapshotPersister} and {@link TaskSnapshotLoader}
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:TaskSnapshotPersisterLoaderTest
+ *  atest WmTests:TaskSnapshotPersisterLoaderTest
  */
 @MediumTest
 @Presubmit
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 2d418ff..ed87f3a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -53,7 +53,7 @@
  * Test class for {@link TaskSnapshotSurface}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:TaskSnapshotSurfaceTest
+ *  atest WmTests:TaskSnapshotSurfaceTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index 8936aad..3c0dd1e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -39,7 +39,7 @@
  * Tests for {@link WindowContainer#forAllWindows} and various implementations.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:WindowContainerTraversalTests
+ *  atest WmTests:WindowContainerTraversalTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index e081ca3..7e248f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -92,7 +92,7 @@
  * Tests for the {@link WindowState} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:WindowStateTests
+ *  atest WmTests:WindowStateTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index ea88315..31a7f24 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -44,6 +44,7 @@
 import android.view.DisplayInfo;
 import android.view.IWindow;
 import android.view.SurfaceControl.Transaction;
+import android.view.View;
 import android.view.WindowManager;
 
 import com.android.server.AttributeCache;
@@ -312,6 +313,16 @@
         }
     }
 
+    static void makeWindowVisible(WindowState... windows) {
+        for (WindowState win : windows) {
+            win.mViewVisibility = View.VISIBLE;
+            win.mRelayoutCalled = true;
+            win.mHasSurface = true;
+            win.mHidden = false;
+            win.showLw(false /* doAnimation */, false /* requestAnim */);
+        }
+    }
+
     /** Creates a {@link ActivityStack} and adds it to the specified {@link DisplayContent}. */
     ActivityStack createTaskStackOnDisplay(DisplayContent dc) {
         return createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java
new file mode 100644
index 0000000..9cda084
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.server.wm.utils;
+
+import static android.graphics.Bitmap.Config.ARGB_8888;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class RotationAnimationUtilsTest {
+
+    private static final int BITMAP_HEIGHT = 100;
+    private static final int BITMAP_WIDTH = 100;
+    private static final int POINT_WIDTH = 1000;
+    private static final int POINT_HEIGHT = 2000;
+
+    private ColorSpace mColorSpace = ColorSpace.get(ColorSpace.Named.DISPLAY_P3);
+    private Matrix mMatrix;
+
+    @Before
+    public void setup() {
+        mMatrix = new Matrix();
+    }
+
+    @Test
+    public void blackLuma() {
+        Bitmap swBitmap = createBitmap(0);
+        GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
+        float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
+        assertEquals(0, borderLuma, 0);
+    }
+
+    @Test
+    public void whiteLuma() {
+        Bitmap swBitmap = createBitmap(1);
+        GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
+        float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
+        assertEquals(1, borderLuma, 0);
+    }
+
+    @Test
+    public void whiteImageBlackBorderLuma() {
+        Bitmap swBitmap = createBitmap(1);
+        setBorderLuma(swBitmap, 0);
+        GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
+        float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
+        assertEquals(0, borderLuma, 0);
+    }
+
+    @Test
+    public void blackImageWhiteBorderLuma() {
+        Bitmap swBitmap = createBitmap(0);
+        setBorderLuma(swBitmap, 1);
+        GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
+        float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
+        assertEquals(1, borderLuma, 0);
+    }
+
+    @Test
+    public void rotate_0_bottomRight() {
+        RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_0,
+                POINT_WIDTH, POINT_HEIGHT, mMatrix);
+        PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT);
+        assertEquals(POINT_WIDTH, newPoints.x, 0);
+        assertEquals(POINT_HEIGHT, newPoints.y, 0);
+    }
+
+    @Test
+    public void rotate_90_bottomRight() {
+        RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_90,
+                POINT_WIDTH, POINT_HEIGHT, mMatrix);
+        PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT);
+        assertEquals(0, newPoints.x, 0);
+        assertEquals(POINT_WIDTH, newPoints.y, 0);
+    }
+
+    @Test
+    public void rotate_180_bottomRight() {
+        RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_180,
+                POINT_WIDTH, POINT_HEIGHT, mMatrix);
+        PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT);
+        assertEquals(0, newPoints.x, 0);
+        assertEquals(0, newPoints.y, 0);
+    }
+
+    @Test
+    public void rotate_270_bottomRight() {
+        RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_270,
+                POINT_WIDTH, POINT_HEIGHT, mMatrix);
+        PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT);
+        assertEquals(POINT_HEIGHT, newPoints.x, 0);
+        assertEquals(0, newPoints.y, 0);
+    }
+
+    private PointF checkMappedPoints(int x, int y) {
+        final float[] fs = new float[] {x, y};
+        mMatrix.mapPoints(fs);
+        return new PointF(fs[0], fs[1]);
+    }
+
+    private Bitmap createBitmap(float luma) {
+        Bitmap bitmap = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, ARGB_8888);
+        for (int i = 0; i < BITMAP_WIDTH; i++) {
+            for (int j = 0; j < BITMAP_HEIGHT; j++) {
+                bitmap.setPixel(i, j, Color.argb(1, luma, luma, luma));
+            }
+        }
+        return bitmap;
+    }
+
+    private GraphicBuffer swBitmapToGraphicsBuffer(Bitmap swBitmap) {
+        Bitmap hwBitmap = swBitmap.copy(Bitmap.Config.HARDWARE, false);
+        return hwBitmap.createGraphicBufferHandle();
+    }
+
+    private void setBorderLuma(Bitmap swBitmap, float luma) {
+        int i;
+        int width = swBitmap.getWidth();
+        int height = swBitmap.getHeight();
+        for (i = 0; i < width; i++) {
+            swBitmap.setPixel(i, 0, Color.argb(1, luma, luma, luma));
+            swBitmap.setPixel(i, height - 1, Color.argb(1, luma, luma, luma));
+        }
+        for (i = 0; i < height; i++) {
+            swBitmap.setPixel(0, i, Color.argb(1, luma, luma, luma));
+            swBitmap.setPixel(width - 1, i, Color.argb(1, luma, luma, luma));
+        }
+    }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
index b75510b..488ee78 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
@@ -83,7 +83,7 @@
  * could transition to INTENT_STARTED.
  *
  * <p> If any bad transition happened, the state becomse UNKNOWN. The UNKNOWN state
- * could be * accumulated, because during the UNKNOWN state more IntentStarted may
+ * could be accumulated, because during the UNKNOWN state more IntentStarted may
  * be triggered. To recover from UNKNOWN to INIT, all the accumualted IntentStarted
  * should termniate.
  *
@@ -100,7 +100,7 @@
   @Override
   public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
     if (state == State.UNKNOWN) {
-      Log.e(TAG, "IntentStarted during UNKNOWN." + intent);
+      Log.wtf(TAG, "IntentStarted during UNKNOWN." + intent);
       incAccIntentStartedEvents();
       return;
     }
@@ -110,32 +110,32 @@
         state != State.ACTIVITY_CANCELLED &&
         state != State.ACTIVITY_FINISHED &&
         state != State.REPORT_FULLY_DRAWN) {
-      Log.e(TAG,
+      Log.wtf(TAG,
           String.format("Cannot transition from %s to %s", state, State.INTENT_STARTED));
       incAccIntentStartedEvents();
       incAccIntentStartedEvents();
       return;
     }
 
-    Log.i(TAG, String.format("Tansition from %s to %s", state, State.INTENT_STARTED));
+    Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_STARTED));
     state = State.INTENT_STARTED;
   }
 
   @Override
   public void onIntentFailed() {
     if (state == State.UNKNOWN) {
-      Log.e(TAG, "IntentFailed during UNKNOWN.");
+      Log.wtf(TAG, "IntentFailed during UNKNOWN.");
       decAccIntentStartedEvents();
       return;
     }
     if (state != State.INTENT_STARTED) {
-      Log.e(TAG,
+      Log.wtf(TAG,
           String.format("Cannot transition from %s to %s", state, State.INTENT_FAILED));
       incAccIntentStartedEvents();
       return;
     }
 
-    Log.i(TAG, String.format("Tansition from %s to %s", state, State.INTENT_FAILED));
+    Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_FAILED));
     state = State.INTENT_FAILED;
   }
 
@@ -143,11 +143,11 @@
   public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
       @Temperature int temperature) {
     if (state == State.UNKNOWN) {
-      Log.e(TAG, "onActivityLaunched during UNKNOWN.");
+      Log.wtf(TAG, "onActivityLaunched during UNKNOWN.");
       return;
     }
     if (state != State.INTENT_STARTED) {
-      Log.e(TAG,
+      Log.wtf(TAG,
           String.format("Cannot transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
       incAccIntentStartedEvents();
       return;
@@ -160,12 +160,12 @@
   @Override
   public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
     if (state == State.UNKNOWN) {
-      Log.e(TAG, "onActivityLaunchCancelled during UNKNOWN.");
+      Log.wtf(TAG, "onActivityLaunchCancelled during UNKNOWN.");
       decAccIntentStartedEvents();
       return;
     }
     if (state != State.ACTIVITY_LAUNCHED) {
-      Log.e(TAG,
+      Log.wtf(TAG,
           String.format("Cannot transition from %s to %s", state, State.ACTIVITY_CANCELLED));
       incAccIntentStartedEvents();
       return;
@@ -179,13 +179,13 @@
   public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
       long timestampNs) {
     if (state == State.UNKNOWN) {
-      Log.e(TAG, "onActivityLaunchFinished during UNKNOWN.");
+      Log.wtf(TAG, "onActivityLaunchFinished during UNKNOWN.");
       decAccIntentStartedEvents();
       return;
     }
 
     if (state != State.ACTIVITY_LAUNCHED) {
-      Log.e(TAG,
+      Log.wtf(TAG,
           String.format("Cannot transition from %s to %s", state, State.ACTIVITY_FINISHED));
       incAccIntentStartedEvents();
       return;
@@ -199,7 +199,7 @@
   public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
       long timestampNs) {
     if (state == State.UNKNOWN) {
-      Log.e(TAG, "onReportFullyDrawn during UNKNOWN.");
+      Log.wtf(TAG, "onReportFullyDrawn during UNKNOWN.");
       return;
     }
     if (state == State.INIT) {
@@ -207,7 +207,7 @@
     }
 
     if (state != State.ACTIVITY_FINISHED) {
-      Log.e(TAG,
+      Log.wtf(TAG,
           String.format("Cannot transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
       return;
     }
@@ -230,7 +230,7 @@
   private void incAccIntentStartedEvents() {
     if (accIntentStartedEvents < 0) {
       throw new AssertionError(
-          String.format("The number of unknows cannot be negative"));
+          String.format("The number of unknowns cannot be negative"));
     }
     if (accIntentStartedEvents == 0) {
       state = State.UNKNOWN;
@@ -243,7 +243,7 @@
   private void decAccIntentStartedEvents() {
     if (accIntentStartedEvents <= 0) {
       throw new AssertionError(
-          String.format("The number of unknows cannot be negative"));
+          String.format("The number of unknowns cannot be negative"));
     }
     if(accIntentStartedEvents == 1) {
       state = State.INIT;
diff --git a/telecomm/java/android/telecom/AudioState.java b/telecomm/java/android/telecom/AudioState.java
index 8b8c86b..ea641f8 100644
--- a/telecomm/java/android/telecom/AudioState.java
+++ b/telecomm/java/android/telecom/AudioState.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 86ad795..a8852a8 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -20,12 +20,11 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 
 import com.android.internal.telecom.IVideoProvider;
diff --git a/telecomm/java/android/telecom/CallerInfo.java b/telecomm/java/android/telecom/CallerInfo.java
index a5d25e2..fb6f994 100644
--- a/telecomm/java/android/telecom/CallerInfo.java
+++ b/telecomm/java/android/telecom/CallerInfo.java
@@ -17,7 +17,7 @@
 package android.telecom;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -41,8 +41,8 @@
 import com.android.i18n.phonenumbers.PhoneNumberUtil;
 import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
 import com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
-
 import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.Locale;
 
 
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 8808339..f205ec6 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -21,9 +21,9 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.Notification;
 import android.bluetooth.BluetoothDevice;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.hardware.camera2.CameraManager;
 import android.net.Uri;
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index 7d4ee76..4f6a9d6 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -16,7 +16,7 @@
 
 package android.telecom;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.net.Uri;
 import android.os.Build;
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index a234bb0..be4e2f4 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -16,22 +16,21 @@
 
 package android.telecom;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.telecom.Call.Details.CallDirection;
 
+import com.android.internal.telecom.IVideoProvider;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
-import com.android.internal.telecom.IVideoProvider;
-
 /**
  * Information about a call that is used between InCallService and Telecom.
  * @hide
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 61a639a1..a427ed6 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -17,8 +17,8 @@
 package android.telecom;
 
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
 import android.bluetooth.BluetoothDevice;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Bundle;
 import android.util.ArrayMap;
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index eb568e0..e1bcb5f 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.os.Build;
 import android.os.Parcel;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index af3c55a..9cf4803 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -26,7 +26,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java
index 4a1aa0a..109e7f8 100644
--- a/telecomm/java/android/telecom/VideoCallImpl.java
+++ b/telecomm/java/android/telecom/VideoCallImpl.java
@@ -16,7 +16,7 @@
 
 package android.telecom;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Handler;
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
index 64e6ca3..4197f3c 100644
--- a/telecomm/java/android/telecom/VideoProfile.java
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -19,7 +19,6 @@
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
-import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index f39981f..aaafee2 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index eb02ea6f..368f8f1 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -25,7 +25,7 @@
 import android.os.RemoteException;
 import android.permission.IPermissionManager;
 import android.provider.Settings;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.ArraySet;
diff --git a/telephony/common/com/android/internal/telephony/GsmAlphabet.java b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
index 5fb4e90..a36ff93 100644
--- a/telephony/common/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
@@ -16,10 +16,10 @@
 
 package com.android.internal.telephony;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.os.Build;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.text.TextUtils;
 import android.util.SparseIntArray;
 
diff --git a/telephony/common/com/android/internal/telephony/HbpcdUtils.java b/telephony/common/com/android/internal/telephony/HbpcdUtils.java
index 2f31942..45a563c 100644
--- a/telephony/common/com/android/internal/telephony/HbpcdUtils.java
+++ b/telephony/common/com/android/internal/telephony/HbpcdUtils.java
@@ -19,7 +19,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.Cursor;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 
 import com.android.internal.telephony.HbpcdLookup.ArbitraryMccSidMatch;
 import com.android.internal.telephony.HbpcdLookup.MccIdd;
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index b302589..afb9b6f 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -40,7 +40,7 @@
 import android.provider.Telephony;
 import android.provider.Telephony.Sms.Intents;
 import android.telephony.PackageChangeReceiver;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
diff --git a/telephony/common/com/android/internal/telephony/SmsConstants.java b/telephony/common/com/android/internal/telephony/SmsConstants.java
index 19f52b0..3aa8bbf 100644
--- a/telephony/common/com/android/internal/telephony/SmsConstants.java
+++ b/telephony/common/com/android/internal/telephony/SmsConstants.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.telephony;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * SMS Constants and must be the same as the corresponding
diff --git a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
index 06c08f5..06c3728 100644
--- a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
+++ b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
@@ -24,7 +24,7 @@
 import android.os.SystemProperties;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 80a55b2..4109ca6 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -28,7 +28,7 @@
 import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.Log;
diff --git a/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
similarity index 100%
rename from telephony/java/com/android/internal/telephony/util/TelephonyUtils.java
rename to telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
diff --git a/telephony/java/android/service/carrier/CarrierIdentifier.java b/telephony/java/android/service/carrier/CarrierIdentifier.java
index af5bf74..7957c25 100644
--- a/telephony/java/android/service/carrier/CarrierIdentifier.java
+++ b/telephony/java/android/service/carrier/CarrierIdentifier.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.uicc.IccUtils;
diff --git a/telephony/java/android/service/euicc/EuiccProfileInfo.java b/telephony/java/android/service/euicc/EuiccProfileInfo.java
index 6c357cc..8450a90 100644
--- a/telephony/java/android/service/euicc/EuiccProfileInfo.java
+++ b/telephony/java/android/service/euicc/EuiccProfileInfo.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.service.carrier.CarrierIdentifier;
diff --git a/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java b/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
index c7a9851..2382f65 100644
--- a/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
+++ b/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
@@ -17,7 +17,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.euicc.DownloadableSubscription;
diff --git a/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java b/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
index abd4065..d0fb511 100644
--- a/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
+++ b/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
@@ -17,7 +17,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.euicc.DownloadableSubscription;
diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java
index 9753d8b..097041f 100644
--- a/telephony/java/android/telephony/AnomalyReporter.java
+++ b/telephony/java/android/telephony/AnomalyReporter.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.content.Context;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fdf8849..dd36a14 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -24,12 +26,11 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.service.carrier.CarrierService;
 import android.telecom.TelecomManager;
 import android.telephony.ims.ImsReasonInfo;
@@ -3399,12 +3400,17 @@
         /** Prefix of all Ims.KEY_* constants. */
         public static final String KEY_PREFIX = "ims.";
 
-        //TODO: Add configs related to IMS.
+        /**
+         * Delay in milliseconds to turn off wifi when IMS is registered over wifi.
+         */
+        public static final String KEY_WIFI_OFF_DEFERRING_TIME_INT =
+                KEY_PREFIX + "wifi_off_deferring_time_int";
 
         private Ims() {}
 
         private static PersistableBundle getDefaults() {
             PersistableBundle defaults = new PersistableBundle();
+            defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_INT, 0);
             return defaults;
         }
     }
@@ -4205,8 +4211,11 @@
     /** @hide */
     @Nullable
     private ICarrierConfigLoader getICarrierConfigLoader() {
-        return ICarrierConfigLoader.Stub
-                .asInterface(ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE));
+        return ICarrierConfigLoader.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getCarrierConfigServiceRegisterer()
+                        .get());
     }
 
     /**
diff --git a/telephony/java/android/telephony/CbGeoUtils.java b/telephony/java/android/telephony/CbGeoUtils.java
index 84be4e8..719ba8d 100644
--- a/telephony/java/android/telephony/CbGeoUtils.java
+++ b/telephony/java/android/telephony/CbGeoUtils.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.text.TextUtils;
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index ce32dce..8e703fe 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -191,6 +193,7 @@
      *
      * @hide
      */
+    @SystemApi
     public abstract @NonNull CellIdentity sanitizeLocationInfo();
 
     @Override
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 2ecdfce..49f425a 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 15c9175..bc46550 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index 2b1387c..acb21f4 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index 4f7c7a9..79a9d44 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index 6d19261..fed3ebf 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java
index f1305f5..58ff8c9 100644
--- a/telephony/java/android/telephony/CellInfoTdscdma.java
+++ b/telephony/java/android/telephony/CellInfoTdscdma.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java
index ee5fec8..33f6a55 100644
--- a/telephony/java/android/telephony/CellInfoWcdma.java
+++ b/telephony/java/android/telephony/CellInfoWcdma.java
@@ -18,7 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 
 import java.util.Objects;
 
diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java
index 64776e3..1193199 100644
--- a/telephony/java/android/telephony/CellLocation.java
+++ b/telephony/java/android/telephony/CellLocation.java
@@ -19,7 +19,6 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.telephony.cdma.CdmaCellLocation;
 import android.telephony.gsm.GsmCellLocation;
 
@@ -38,7 +37,11 @@
      */
     public static void requestLocationUpdate() {
         try {
-            ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
+            ITelephony phone = ITelephony.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getTelephonyServiceRegisterer()
+                            .get());
             if (phone != null) {
                 phone.updateServiceLocation();
             }
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index 1998439..cab3b0c 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -20,7 +20,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 
 import java.util.Objects;
 
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index a9f3487..28052aa 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.IntRange;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index a6ba9c2..2ef2a52 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.IntRange;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index f31fafe..4d67bcf 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.os.Parcel;
@@ -155,7 +157,17 @@
      * @param ss signal strength from modem.
      */
     public CellSignalStrengthNr(android.hardware.radio.V1_4.NrSignalStrength ss) {
-        this(ss.csiRsrp, ss.csiRsrq, ss.csiSinr, ss.ssRsrp, ss.ssRsrq, ss.ssSinr);
+        this(flip(ss.csiRsrp), flip(ss.csiRsrq), ss.csiSinr, flip(ss.ssRsrp), flip(ss.ssRsrq),
+                ss.ssSinr);
+    }
+
+    /**
+     * Flip sign cell strength value when taking in the value from hal
+     * @param val cell strength value
+     * @return flipped value
+     */
+    private static int flip(int val) {
+        return val != CellInfo.UNAVAILABLE ? -val : val;
     }
 
     /**
diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
index f4a3dbb..3bd9d58 100644
--- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.os.Parcel;
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 34b1385..535e952 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.IntRange;
 import android.annotation.StringDef;
 import android.compat.annotation.UnsupportedAppUsage;
diff --git a/telephony/java/android/telephony/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java
index 202da68..a6dedf7 100644
--- a/telephony/java/android/telephony/NetworkScan.java
+++ b/telephony/java/android/telephony/NetworkScan.java
@@ -16,10 +16,10 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.IntDef;
-import android.content.Context;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 
 import com.android.internal.telephony.ITelephony;
 
@@ -148,6 +148,9 @@
 
     private ITelephony getITelephony() {
         return ITelephony.Stub.asInterface(
-            ServiceManager.getService(Context.TELEPHONY_SERVICE));
+            TelephonyFrameworkInitializer
+                    .getTelephonyServiceManager()
+                    .getTelephonyServiceRegisterer()
+                    .get());
     }
 }
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index 8c5e107..844289c 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java
index 89b9665..214ab41 100644
--- a/telephony/java/android/telephony/NetworkServiceCallback.java
+++ b/telephony/java/android/telephony/NetworkServiceCallback.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 6e86a42..2f9e6ac 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 5b12aed..2c62d06 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -2044,4 +2046,27 @@
     public boolean isIwlanPreferred() {
         return mIsIwlanPreferred;
     }
+     /**
+     * @return {@code true}Returns True whenever the modem is searching for service.
+     * To check both CS and PS domain
+     */
+
+    public boolean isSearching() {
+        NetworkRegistrationInfo psRegState = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+        if (psRegState != null && psRegState.getRegistrationState()
+                == NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING) {
+            return true;
+        }
+
+        NetworkRegistrationInfo csRegState = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+        if (csRegState != null && csRegState.getRegistrationState()
+                == NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING) {
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 3350a33..1f7d55f 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index fbe355e..db33be3 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -35,7 +35,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -745,7 +744,11 @@
                     "Invalid pdu format. format must be either 3gpp or 3gpp2");
         }
         try {
-            ISms iSms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            ISms iSms = ISms.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSmsServiceRegisterer()
+                            .get());
             if (iSms != null) {
                 iSms.injectSmsPduForSubscriber(
                         getSubscriptionId(), pdu, format, receivedIntent);
@@ -1535,7 +1538,10 @@
 
     private static ITelephony getITelephony() {
         ITelephony binder = ITelephony.Stub.asInterface(
-                ServiceManager.getService(Context.TELEPHONY_SERVICE));
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getTelephonyServiceRegisterer()
+                        .get());
         if (binder == null) {
             throw new RuntimeException("Could not find Telephony Service.");
         }
@@ -1573,7 +1579,11 @@
     }
 
     private static ISms getISmsService() {
-        return ISms.Stub.asInterface(ServiceManager.getService("isms"));
+        return ISms.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getSmsServiceRegisterer()
+                        .get());
     }
 
     /**
@@ -2007,7 +2017,11 @@
     public boolean isSMSPromptEnabled() {
         ISms iSms = null;
         try {
-            iSms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            iSms = ISms.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSmsServiceRegisterer()
+                            .get());
             return iSms.isSMSPromptEnabled();
         } catch (RemoteException ex) {
             return false;
@@ -2434,14 +2448,18 @@
      * @param sentIntent if not NULL this <code>PendingIntent</code> is
      *  broadcast when the message is successfully sent, or failed
      * @throws IllegalArgumentException if contentUri is empty
+     * @deprecated use {@link MmsManager#sendMultimediaMessage} instead.
      */
     public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
             Bundle configOverrides, PendingIntent sentIntent) {
         if (contentUri == null) {
             throw new IllegalArgumentException("Uri contentUri null");
         }
-        MmsManager.getInstance().sendMultimediaMessage(getSubscriptionId(), contentUri,
-                    locationUrl, configOverrides, sentIntent);
+        MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
+        if (m != null) {
+            m.sendMultimediaMessage(getSubscriptionId(), contentUri, locationUrl, configOverrides,
+                    sentIntent);
+        }
     }
 
     /**
@@ -2465,6 +2483,7 @@
      * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
      *  broadcast when the message is downloaded, or the download is failed
      * @throws IllegalArgumentException if locationUrl or contentUri is empty
+     * @deprecated use {@link MmsManager#downloadMultimediaMessage} instead.
      */
     public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri,
             Bundle configOverrides, PendingIntent downloadedIntent) {
@@ -2474,8 +2493,11 @@
         if (contentUri == null) {
             throw new IllegalArgumentException("Uri contentUri null");
         }
-        MmsManager.getInstance().downloadMultimediaMessage(getSubscriptionId(), locationUrl,
-                contentUri, configOverrides, downloadedIntent);
+        MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
+        if (m != null) {
+            m.downloadMultimediaMessage(getSubscriptionId(), locationUrl, contentUri,
+                    configOverrides, downloadedIntent);
+        }
     }
 
     // MMS send/download failure result codes
@@ -2517,9 +2539,9 @@
      * </p>
      *
      * @return the bundle key/values pairs that contains MMS configuration values
+     *  or an empty bundle if they cannot be found.
      */
-    @Nullable
-    public Bundle getCarrierConfigValues() {
+    @NonNull public Bundle getCarrierConfigValues() {
         try {
             ISms iSms = getISmsService();
             if (iSms != null) {
@@ -2528,7 +2550,7 @@
         } catch (RemoteException ex) {
             // ignore it
         }
-        return null;
+        return new Bundle();
     }
 
     /**
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index c0bc29d..c217b8b 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
 
 import android.annotation.Nullable;
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 2c0a1c9..c24eeb7 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 22eed6e..4510fed 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED;
 import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
 
@@ -53,7 +55,6 @@
 import android.os.ParcelUuid;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.provider.Telephony.SimInfo;
 import android.telephony.euicc.EuiccManager;
 import android.telephony.ims.ImsMmTelManager;
@@ -265,7 +266,7 @@
      * <P>Type: TEXT (String)</P>
      */
     /** @hide */
-    public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
+    public static final String UNIQUE_KEY_SUBSCRIPTION_ID = SimInfo.UNIQUE_KEY_SUBSCRIPTION_ID;
 
     /**
      * TelephonyProvider column name for a unique identifier for the subscription within the
@@ -274,18 +275,18 @@
      * <P>Type: TEXT (String)</P>
      */
     /** @hide */
-    public static final String ICC_ID = "icc_id";
+    public static final String ICC_ID = SimInfo.ICC_ID;
 
     /**
      * TelephonyProvider column name for user SIM_SlOT_INDEX
      * <P>Type: INTEGER (int)</P>
      */
     /** @hide */
-    public static final String SIM_SLOT_INDEX = "sim_id";
+    public static final String SIM_SLOT_INDEX = SimInfo.SIM_SLOT_INDEX;
 
     /** SIM is not inserted */
     /** @hide */
-    public static final int SIM_NOT_INSERTED = -1;
+    public static final int SIM_NOT_INSERTED = SimInfo.SIM_NOT_INSERTED;
 
     /**
      * The slot-index for Bluetooth Remote-SIM subscriptions
@@ -300,23 +301,7 @@
      * Default value is 0.
      */
     /** @hide */
-    public static final String SUBSCRIPTION_TYPE = "subscription_type";
-
-    /**
-     * TelephonyProvider column name white_listed_apn_data.
-     * It's a bitmask of APN types that will be allowed on this subscription even if it's metered
-     * and mobile data is turned off by the user.
-     * <P>Type: INTEGER (int)</P> For example, if TYPE_MMS is is true, Telephony will allow MMS
-     * data connection to setup even if MMS is metered and mobile_data is turned off on that
-     * subscription.
-     *
-     * Default value is 0.
-     *
-     * @deprecated Replaced by {@link #DATA_ENABLED_OVERRIDE_RULES}
-     * @hide
-     */
-    @Deprecated
-    public static final String WHITE_LISTED_APN_DATA = "white_listed_apn_data";
+    public static final String SUBSCRIPTION_TYPE = SimInfo.SUBSCRIPTION_TYPE;
 
     /**
      * TelephonyProvider column name data_enabled_override_rules.
@@ -329,7 +314,15 @@
      *
      * @hide
      */
-    public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules";
+    public static final String DATA_ENABLED_OVERRIDE_RULES = SimInfo.DATA_ENABLED_OVERRIDE_RULES;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"SUBSCRIPTION_TYPE_"},
+        value = {
+            SUBSCRIPTION_TYPE_LOCAL_SIM,
+            SUBSCRIPTION_TYPE_REMOTE_SIM})
+    public @interface SubscriptionType {}
 
     /**
      * This constant is to designate a subscription as a Local-SIM Subscription.
@@ -337,7 +330,7 @@
      * device.
      * </p>
      */
-    public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0;
+    public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = SimInfo.SUBSCRIPTION_TYPE_LOCAL_SIM;
 
     /**
      * This constant is to designate a subscription as a Remote-SIM Subscription.
@@ -363,29 +356,21 @@
      * was never seen before.
      * </p>
      */
-    public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1;
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"SUBSCRIPTION_TYPE_"},
-        value = {
-            SUBSCRIPTION_TYPE_LOCAL_SIM,
-            SUBSCRIPTION_TYPE_REMOTE_SIM})
-    public @interface SubscriptionType {}
+    public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = SimInfo.SUBSCRIPTION_TYPE_REMOTE_SIM;
 
     /**
      * TelephonyProvider column name for user displayed name.
      * <P>Type: TEXT (String)</P>
      */
     /** @hide */
-    public static final String DISPLAY_NAME = "display_name";
+    public static final String DISPLAY_NAME = SimInfo.DISPLAY_NAME;
 
     /**
      * TelephonyProvider column name for the service provider name for the SIM.
      * <P>Type: TEXT (String)</P>
      */
     /** @hide */
-    public static final String CARRIER_NAME = "carrier_name";
+    public static final String CARRIER_NAME = SimInfo.CARRIER_NAME;
 
     /**
      * Default name resource
@@ -399,44 +384,44 @@
      *
      * @hide
      */
-    public static final String NAME_SOURCE = "name_source";
+    public static final String NAME_SOURCE = SimInfo.NAME_SOURCE;
 
     /**
      * The name_source is the default, which is from the carrier id.
      * @hide
      */
-    public static final int NAME_SOURCE_DEFAULT_SOURCE = 0;
+    public static final int NAME_SOURCE_DEFAULT = SimInfo.NAME_SOURCE_DEFAULT;
 
     /**
      * The name_source is from SIM EF_SPN.
      * @hide
      */
-    public static final int NAME_SOURCE_SIM_SPN = 1;
+    public static final int NAME_SOURCE_SIM_SPN = SimInfo.NAME_SOURCE_SIM_SPN;
 
     /**
      * The name_source is from user input
      * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public static final int NAME_SOURCE_USER_INPUT = 2;
+    public static final int NAME_SOURCE_USER_INPUT = SimInfo.NAME_SOURCE_USER_INPUT;
 
     /**
      * The name_source is carrier (carrier app, carrier config, etc.)
      * @hide
      */
-    public static final int NAME_SOURCE_CARRIER = 3;
+    public static final int NAME_SOURCE_CARRIER = SimInfo.NAME_SOURCE_CARRIER;
 
     /**
      * The name_source is from SIM EF_PNN.
      * @hide
      */
-    public static final int NAME_SOURCE_SIM_PNN = 4;
+    public static final int NAME_SOURCE_SIM_PNN = SimInfo.NAME_SOURCE_SIM_PNN;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"NAME_SOURCE_"},
             value = {
-                    NAME_SOURCE_DEFAULT_SOURCE,
+                    NAME_SOURCE_DEFAULT,
                     NAME_SOURCE_SIM_SPN,
                     NAME_SOURCE_USER_INPUT,
                     NAME_SOURCE_CARRIER,
@@ -449,67 +434,30 @@
      * <P>Type: INTEGER (int)</P>
      */
     /** @hide */
-    public static final String COLOR = "color";
-
-    /** @hide */
-    public static final int COLOR_1 = 0;
-
-    /** @hide */
-    public static final int COLOR_2 = 1;
-
-    /** @hide */
-    public static final int COLOR_3 = 2;
-
-    /** @hide */
-    public static final int COLOR_4 = 3;
-
-    /** @hide */
-    public static final int COLOR_DEFAULT = COLOR_1;
+    public static final String COLOR = SimInfo.COLOR;
 
     /**
      * TelephonyProvider column name for the phone number of a SIM.
      * <P>Type: TEXT (String)</P>
      */
     /** @hide */
-    public static final String NUMBER = "number";
+    public static final String NUMBER = SimInfo.NUMBER;
 
     /**
-     * TelephonyProvider column name for the number display format of a SIM.
+     * TelephonyProvider column name for whether data roaming is enabled.
      * <P>Type: INTEGER (int)</P>
      */
     /** @hide */
-    public static final String DISPLAY_NUMBER_FORMAT = "display_number_format";
-
-    /** @hide */
-    public static final int DISPLAY_NUMBER_NONE = 0;
-
-    /** @hide */
-    public static final int DISPLAY_NUMBER_FIRST = 1;
-
-    /** @hide */
-    public static final int DISPLAY_NUMBER_LAST = 2;
-
-    /** @hide */
-    public static final int DISPLAY_NUMBER_DEFAULT = DISPLAY_NUMBER_FIRST;
-
-    /**
-     * TelephonyProvider column name for permission for data roaming of a SIM.
-     * <P>Type: INTEGER (int)</P>
-     */
-    /** @hide */
-    public static final String DATA_ROAMING = "data_roaming";
+    public static final String DATA_ROAMING = SimInfo.DATA_ROAMING;
 
     /** Indicates that data roaming is enabled for a subscription */
-    public static final int DATA_ROAMING_ENABLE = 1;
+    public static final int DATA_ROAMING_ENABLE = SimInfo.DATA_ROAMING_ENABLE;
 
     /** Indicates that data roaming is disabled for a subscription */
-    public static final int DATA_ROAMING_DISABLE = 0;
+    public static final int DATA_ROAMING_DISABLE = SimInfo.DATA_ROAMING_DISABLE;
 
     /** @hide */
-    public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE;
-
-    /** @hide */
-    public static final int SIM_PROVISIONED = 0;
+    public static final int DATA_ROAMING_DEFAULT = SimInfo.DATA_ROAMING_DEFAULT;
 
     /**
      * TelephonyProvider column name for subscription carrier id.
@@ -517,61 +465,61 @@
      * <p>Type: INTEGER (int) </p>
      * @hide
      */
-    public static final String CARRIER_ID = "carrier_id";
+    public static final String CARRIER_ID = SimInfo.CARRIER_ID;
 
     /**
      * @hide A comma-separated list of EHPLMNs associated with the subscription
      * <P>Type: TEXT (String)</P>
      */
-    public static final String EHPLMNS = "ehplmns";
+    public static final String EHPLMNS = SimInfo.EHPLMNS;
 
     /**
      * @hide A comma-separated list of HPLMNs associated with the subscription
      * <P>Type: TEXT (String)</P>
      */
-    public static final String HPLMNS = "hplmns";
+    public static final String HPLMNS = SimInfo.HPLMNS;
 
     /**
      * TelephonyProvider column name for the MCC associated with a SIM, stored as a string.
      * <P>Type: TEXT (String)</P>
      * @hide
      */
-    public static final String MCC_STRING = "mcc_string";
+    public static final String MCC_STRING = SimInfo.MCC_STRING;
 
     /**
      * TelephonyProvider column name for the MNC associated with a SIM, stored as a string.
      * <P>Type: TEXT (String)</P>
      * @hide
      */
-    public static final String MNC_STRING = "mnc_string";
+    public static final String MNC_STRING = SimInfo.MNC_STRING;
 
     /**
      * TelephonyProvider column name for the MCC associated with a SIM.
      * <P>Type: INTEGER (int)</P>
      * @hide
      */
-    public static final String MCC = "mcc";
+    public static final String MCC = SimInfo.MCC;
 
     /**
      * TelephonyProvider column name for the MNC associated with a SIM.
      * <P>Type: INTEGER (int)</P>
      * @hide
      */
-    public static final String MNC = "mnc";
+    public static final String MNC = SimInfo.MNC;
 
     /**
      * TelephonyProvider column name for the iso country code associated with a SIM.
      * <P>Type: TEXT (String)</P>
      * @hide
      */
-    public static final String ISO_COUNTRY_CODE = "iso_country_code";
+    public static final String ISO_COUNTRY_CODE = SimInfo.ISO_COUNTRY_CODE;
 
     /**
      * TelephonyProvider column name for the sim provisioning status associated with a SIM.
      * <P>Type: INTEGER (int)</P>
      * @hide
      */
-    public static final String SIM_PROVISIONING_STATUS = "sim_provisioning_status";
+    public static final String SIM_PROVISIONING_STATUS = SimInfo.SIM_PROVISIONING_STATUS;
 
     /**
      * TelephonyProvider column name for whether a subscription is embedded (that is, present on an
@@ -579,7 +527,7 @@
      * <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded.
      * @hide
      */
-    public static final String IS_EMBEDDED = "is_embedded";
+    public static final String IS_EMBEDDED = SimInfo.IS_EMBEDDED;
 
     /**
      * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the
@@ -587,7 +535,7 @@
      * <P>Type: TEXT (String)</P>
      * @hide
      */
-    public static final String CARD_ID = "card_id";
+    public static final String CARD_ID = SimInfo.CARD_ID;
 
     /**
      * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
@@ -595,7 +543,7 @@
      * <p>TYPE: BLOB
      * @hide
      */
-    public static final String ACCESS_RULES = "access_rules";
+    public static final String ACCESS_RULES = SimInfo.ACCESS_RULES;
 
     /**
      * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
@@ -605,7 +553,7 @@
      * @hide
      */
     public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS =
-            "access_rules_from_carrier_configs";
+            SimInfo.ACCESS_RULES_FROM_CARRIER_CONFIGS;
 
     /**
      * TelephonyProvider column name identifying whether an embedded subscription is on a removable
@@ -615,79 +563,79 @@
      * <p>TYPE: INTEGER (int), 1 for removable or 0 for non-removable.
      * @hide
      */
-    public static final String IS_REMOVABLE = "is_removable";
+    public static final String IS_REMOVABLE = SimInfo.IS_REMOVABLE;
 
     /**
      *  TelephonyProvider column name for extreme threat in CB settings
      * @hide
      */
-    public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts";
+    public static final String CB_EXTREME_THREAT_ALERT = SimInfo.CB_EXTREME_THREAT_ALERT;
 
     /**
      * TelephonyProvider column name for severe threat in CB settings
      *@hide
      */
-    public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts";
+    public static final String CB_SEVERE_THREAT_ALERT = SimInfo.CB_SEVERE_THREAT_ALERT;
 
     /**
      * TelephonyProvider column name for amber alert in CB settings
      *@hide
      */
-    public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts";
+    public static final String CB_AMBER_ALERT = SimInfo.CB_AMBER_ALERT;
 
     /**
      * TelephonyProvider column name for emergency alert in CB settings
      *@hide
      */
-    public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts";
+    public static final String CB_EMERGENCY_ALERT = SimInfo.CB_EMERGENCY_ALERT;
 
     /**
      * TelephonyProvider column name for alert sound duration in CB settings
      *@hide
      */
-    public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration";
+    public static final String CB_ALERT_SOUND_DURATION = SimInfo.CB_ALERT_SOUND_DURATION;
 
     /**
      * TelephonyProvider column name for alert reminder interval in CB settings
      *@hide
      */
-    public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
+    public static final String CB_ALERT_REMINDER_INTERVAL = SimInfo.CB_ALERT_REMINDER_INTERVAL;
 
     /**
      * TelephonyProvider column name for enabling vibrate in CB settings
      *@hide
      */
-    public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate";
+    public static final String CB_ALERT_VIBRATE = SimInfo.CB_ALERT_VIBRATE;
 
     /**
      * TelephonyProvider column name for enabling alert speech in CB settings
      *@hide
      */
-    public static final String CB_ALERT_SPEECH = "enable_alert_speech";
+    public static final String CB_ALERT_SPEECH = SimInfo.CB_ALERT_SPEECH;
 
     /**
      * TelephonyProvider column name for ETWS test alert in CB settings
      *@hide
      */
-    public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
+    public static final String CB_ETWS_TEST_ALERT = SimInfo.CB_ETWS_TEST_ALERT;
 
     /**
      * TelephonyProvider column name for enable channel50 alert in CB settings
      *@hide
      */
-    public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
+    public static final String CB_CHANNEL_50_ALERT = SimInfo.CB_CHANNEL_50_ALERT;
 
     /**
      * TelephonyProvider column name for CMAS test alert in CB settings
      *@hide
      */
-    public static final String CB_CMAS_TEST_ALERT= "enable_cmas_test_alerts";
+    public static final String CB_CMAS_TEST_ALERT = SimInfo.CB_CMAS_TEST_ALERT;
 
     /**
      * TelephonyProvider column name for Opt out dialog in CB settings
      *@hide
      */
-    public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
+    public static final String CB_OPT_OUT_DIALOG = SimInfo.CB_OPT_OUT_DIALOG;
 
     /**
      * TelephonyProvider column name for enable Volte.
@@ -696,37 +644,37 @@
      * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
      *@hide
      */
-    public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+    public static final String ENHANCED_4G_MODE_ENABLED = SimInfo.ENHANCED_4G_MODE_ENABLED;
 
     /**
      * TelephonyProvider column name for enable VT (Video Telephony over IMS)
      *@hide
      */
-    public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+    public static final String VT_IMS_ENABLED = SimInfo.VT_IMS_ENABLED;
 
     /**
      * TelephonyProvider column name for enable Wifi calling
      *@hide
      */
-    public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+    public static final String WFC_IMS_ENABLED = SimInfo.WFC_IMS_ENABLED;
 
     /**
      * TelephonyProvider column name for Wifi calling mode
      *@hide
      */
-    public static final String WFC_IMS_MODE = "wfc_ims_mode";
+    public static final String WFC_IMS_MODE = SimInfo.WFC_IMS_MODE;
 
     /**
      * TelephonyProvider column name for Wifi calling mode in roaming
      *@hide
      */
-    public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
+    public static final String WFC_IMS_ROAMING_MODE = SimInfo.WFC_IMS_ROAMING_MODE;
 
     /**
      * TelephonyProvider column name for enable Wifi calling in roaming
      *@hide
      */
-    public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+    public static final String WFC_IMS_ROAMING_ENABLED = SimInfo.WFC_IMS_ROAMING_ENABLED;
 
     /**
      * TelephonyProvider column name for whether a subscription is opportunistic, that is,
@@ -735,7 +683,7 @@
      * <p>Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic.
      * @hide
      */
-    public static final String IS_OPPORTUNISTIC = "is_opportunistic";
+    public static final String IS_OPPORTUNISTIC = SimInfo.IS_OPPORTUNISTIC;
 
     /**
      * TelephonyProvider column name for group ID. Subscriptions with same group ID
@@ -744,7 +692,7 @@
      *
      * @hide
      */
-    public static final String GROUP_UUID = "group_uuid";
+    public static final String GROUP_UUID = SimInfo.GROUP_UUID;
 
     /**
      * TelephonyProvider column name for group owner. It's the package name who created
@@ -752,14 +700,7 @@
      *
      * @hide
      */
-    public static final String GROUP_OWNER = "group_owner";
-
-    /**
-     * TelephonyProvider column name for whether a subscription is metered or not, that is, whether
-     * the network it connects to charges for subscription or not. For example, paid CBRS or unpaid.
-     * @hide
-     */
-    public static final String IS_METERED = "is_metered";
+    public static final String GROUP_OWNER = SimInfo.GROUP_OWNER;
 
     /**
      * TelephonyProvider column name for the profile class of a subscription
@@ -767,7 +708,7 @@
      * <P>Type: INTEGER (int)</P>
      * @hide
      */
-    public static final String PROFILE_CLASS = "profile_class";
+    public static final String PROFILE_CLASS = SimInfo.PROFILE_CLASS;
 
     /**
      * Profile class of the subscription
@@ -775,11 +716,11 @@
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "PROFILE_CLASS_" }, value = {
-            PROFILE_CLASS_TESTING,
-            PROFILE_CLASS_PROVISIONING,
-            PROFILE_CLASS_OPERATIONAL,
-            PROFILE_CLASS_UNSET,
-            PROFILE_CLASS_DEFAULT
+            SimInfo.PROFILE_CLASS_TESTING,
+            SimInfo.PROFILE_CLASS_PROVISIONING,
+            SimInfo.PROFILE_CLASS_OPERATIONAL,
+            SimInfo.PROFILE_CLASS_UNSET,
+            SimInfo.PROFILE_CLASS_DEFAULT
     })
     public @interface ProfileClass {}
 
@@ -791,7 +732,7 @@
      * @hide
      */
     @SystemApi
-    public static final int PROFILE_CLASS_TESTING = 0;
+    public static final int PROFILE_CLASS_TESTING = SimInfo.PROFILE_CLASS_TESTING;
 
     /**
      * A provisioning profile is pre-loaded onto the eUICC and
@@ -800,7 +741,7 @@
      * @hide
      */
     @SystemApi
-    public static final int PROFILE_CLASS_PROVISIONING = 1;
+    public static final int PROFILE_CLASS_PROVISIONING = SimInfo.PROFILE_CLASS_PROVISIONING;
 
     /**
      * An operational profile can be pre-loaded or downloaded
@@ -809,7 +750,7 @@
      * @hide
      */
     @SystemApi
-    public static final int PROFILE_CLASS_OPERATIONAL = 2;
+    public static final int PROFILE_CLASS_OPERATIONAL = SimInfo.PROFILE_CLASS_OPERATIONAL;
 
     /**
      * The profile class is unset. This occurs when profile class
@@ -818,14 +759,14 @@
      * @hide
      */
     @SystemApi
-    public static final int PROFILE_CLASS_UNSET = -1;
+    public static final int PROFILE_CLASS_UNSET = SimInfo.PROFILE_CLASS_UNSET;
 
     /**
      * Default profile class
      * @hide
      */
     @SystemApi
-    public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET;
+    public static final int PROFILE_CLASS_DEFAULT = SimInfo.PROFILE_CLASS_DEFAULT;
 
     /**
      * IMSI (International Mobile Subscriber Identity).
@@ -833,13 +774,13 @@
      * @hide
      */
     //TODO: add @SystemApi
-    public static final String IMSI = "imsi";
+    public static final String IMSI = SimInfo.IMSI;
 
     /**
      * Whether uicc applications is set to be enabled or disabled. By default it's enabled.
      * @hide
      */
-    public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled";
+    public static final String UICC_APPLICATIONS_ENABLED = SimInfo.UICC_APPLICATIONS_ENABLED;
 
     /**
      * Broadcast Action: The user has changed one of the default subs related to
@@ -1023,8 +964,11 @@
 
     private final INetworkPolicyManager getNetworkPolicy() {
         if (mNetworkPolicy == null) {
-            mNetworkPolicy = INetworkPolicyManager.Stub
-                    .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+            mNetworkPolicy = INetworkPolicyManager.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getNetworkPolicyServiceRegisterer()
+                            .get());
         }
         return mNetworkPolicy;
     }
@@ -1041,6 +985,23 @@
      */
     public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
         if (listener == null) return;
+        addOnSubscriptionsChangedListener(listener.mExecutor, listener);
+    }
+
+    /**
+     * Register for changes to the list of active {@link SubscriptionInfo} records or to the
+     * individual records themselves. When a change occurs the onSubscriptionsChanged method of
+     * the listener will be invoked immediately if there has been a notification. The
+     * onSubscriptionChanged method will also be triggered once initially when calling this
+     * function.
+     *
+     * @param listener an instance of {@link OnSubscriptionsChangedListener} with
+     *                 onSubscriptionsChanged overridden.
+     * @param executor the executor that will execute callbacks.
+     */
+    public void addOnSubscriptionsChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnSubscriptionsChangedListener listener) {
         String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         if (DBG) {
             logd("register OnSubscriptionsChangedListener pkgName=" + pkgName
@@ -1052,7 +1013,7 @@
                 mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
         if (telephonyRegistryManager != null) {
             telephonyRegistryManager.addOnSubscriptionsChangedListener(listener,
-                    listener.mExecutor);
+                    executor);
         }
     }
 
@@ -1185,7 +1146,11 @@
         SubscriptionInfo subInfo = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1219,7 +1184,11 @@
         SubscriptionInfo result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1253,7 +1222,11 @@
         SubscriptionInfo result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
                         mContext.getOpPackageName(), mContext.getFeatureId());
@@ -1277,7 +1250,11 @@
         List<SubscriptionInfo> result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 result = iSub.getAllSubInfoList(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1358,7 +1335,11 @@
         List<SubscriptionInfo> activeList = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1409,7 +1390,11 @@
         List<SubscriptionInfo> result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1448,7 +1433,11 @@
         List<SubscriptionInfo> result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 result = iSub.getAccessibleSubscriptionInfoList(mContext.getOpPackageName());
             }
@@ -1477,7 +1466,11 @@
     public void requestEmbeddedSubscriptionInfoListRefresh() {
         int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc();
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
             }
@@ -1506,7 +1499,11 @@
     @SystemApi
     public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
             }
@@ -1527,7 +1524,11 @@
         int result = 0;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 result = iSub.getAllSubInfoCount(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1556,7 +1557,11 @@
         int result = 0;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1577,7 +1582,11 @@
         int result = 0;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 result = iSub.getActiveSubInfoCountMax();
             }
@@ -1634,7 +1643,11 @@
         }
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub == null) {
                 Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- ISub service is null");
                 return;
@@ -1668,7 +1681,11 @@
         }
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub == null) {
                 Log.e(LOG_TAG, "[removeSubscriptionInfoRecord]- ISub service is null");
                 return;
@@ -1771,7 +1788,11 @@
         int result = INVALID_SIM_SLOT_INDEX;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 result = iSub.getSlotIndex(subscriptionId);
             }
@@ -1805,7 +1826,11 @@
         int[] subId = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 subId = iSub.getSubId(slotIndex);
             }
@@ -1829,7 +1854,11 @@
         int result = INVALID_PHONE_INDEX;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 result = iSub.getPhoneId(subId);
             }
@@ -1863,7 +1892,11 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 subId = iSub.getDefaultSubId();
             }
@@ -1886,7 +1919,11 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 subId = iSub.getDefaultVoiceSubId();
             }
@@ -1916,7 +1953,11 @@
     public void setDefaultVoiceSubscriptionId(int subscriptionId) {
         if (VDBG) logd("setDefaultVoiceSubId sub id = " + subscriptionId);
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 iSub.setDefaultVoiceSubId(subscriptionId);
             }
@@ -1964,7 +2005,11 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 subId = iSub.getDefaultSmsSubId();
             }
@@ -1990,7 +2035,11 @@
     public void setDefaultSmsSubId(int subscriptionId) {
         if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId);
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 iSub.setDefaultSmsSubId(subscriptionId);
             }
@@ -2028,7 +2077,11 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 subId = iSub.getDefaultDataSubId();
             }
@@ -2054,7 +2107,11 @@
     public void setDefaultDataSubId(int subscriptionId) {
         if (VDBG) logd("setDataSubscription sub id = " + subscriptionId);
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 iSub.setDefaultDataSubId(subscriptionId);
             }
@@ -2085,7 +2142,11 @@
     /** @hide */
     public void clearSubscriptionInfo() {
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 iSub.clearSubInfo();
             }
@@ -2221,7 +2282,11 @@
      */
     public @NonNull int[] getActiveSubscriptionIdList(boolean visibleOnly) {
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 int[] subId = iSub.getActiveSubIdList(visibleOnly);
                 if (subId != null) return subId;
@@ -2272,7 +2337,11 @@
         int simState = TelephonyManager.SIM_STATE_UNKNOWN;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 simState = iSub.getSimStateForSlotIndex(slotIndex);
             }
@@ -2291,7 +2360,11 @@
      */
     public static void setSubscriptionProperty(int subId, String propKey, String propValue) {
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 iSub.setSubscriptionProperty(subId, propKey, propValue);
             }
@@ -2311,7 +2384,11 @@
             Context context) {
         String resultValue = null;
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 resultValue = iSub.getSubscriptionProperty(subId, propKey,
                         context.getOpPackageName(), context.getFeatureId());
@@ -2453,7 +2530,11 @@
     @UnsupportedAppUsage
     public boolean isActiveSubId(int subId) {
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 return iSub.isActiveSubId(subId, mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -2756,7 +2837,11 @@
             @TelephonyManager.SetOpportunisticSubscriptionResult Consumer<Integer> callback) {
         if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId);
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub == null) return;
 
             ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() {
@@ -2799,7 +2884,11 @@
     public int getPreferredDataSubscriptionId() {
         int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 preferredSubId = iSub.getPreferredDataSubscriptionId();
             }
@@ -2830,7 +2919,11 @@
         List<SubscriptionInfo> subInfoList = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, contextFeature);
             }
@@ -2931,7 +3024,11 @@
         ParcelUuid groupUuid = null;
         int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 groupUuid = iSub.createSubscriptionGroup(subIdArray, pkgForDebug);
             } else {
@@ -2981,7 +3078,11 @@
         int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 iSub.addSubscriptionsIntoGroup(subIdArray, groupUuid, pkgForDebug);
             } else {
@@ -3033,7 +3134,11 @@
         int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 iSub.removeSubscriptionsFromGroup(subIdArray, groupUuid, pkgForDebug);
             } else {
@@ -3078,7 +3183,11 @@
 
         List<SubscriptionInfo> result = null;
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, contextFeature);
             } else {
@@ -3191,7 +3300,11 @@
             logd("setSubscriptionActivated subId= " + subscriptionId + " enable " + enable);
         }
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 return iSub.setSubscriptionEnabled(enable, subscriptionId);
             }
@@ -3221,7 +3334,11 @@
             logd("setUiccApplicationsEnabled subId= " + subscriptionId + " enable " + enabled);
         }
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 iSub.setUiccApplicationsEnabled(enabled, subscriptionId);
             }
@@ -3251,7 +3368,11 @@
             logd("canDisablePhysicalSubscription");
         }
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 return iSub.canDisablePhysicalSubscription();
             }
@@ -3272,7 +3393,11 @@
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isSubscriptionEnabled(int subscriptionId) {
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 return iSub.isSubscriptionEnabled(subscriptionId);
             }
@@ -3295,7 +3420,11 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 subId = iSub.getEnabledSubscriptionId(slotIndex);
             }
@@ -3321,7 +3450,11 @@
         int result = 0;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 result = helper.callMethod(iSub);
             }
@@ -3344,7 +3477,11 @@
      */
     public static int getActiveDataSubscriptionId() {
         try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            ISub iSub = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
             if (iSub != null) {
                 return iSub.getActiveDataSubscriptionId();
             }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index df8c26d..843c065 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -60,7 +60,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.WorkSource;
 import android.provider.Settings.SettingNotFoundException;
@@ -107,6 +106,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.SmsApplication;
+import com.android.telephony.Rlog;
 
 import dalvik.system.VMRuntime;
 
@@ -785,30 +785,6 @@
     public static final String EXTRA_PRECISE_DISCONNECT_CAUSE = "precise_disconnect_cause";
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for an String containing the data APN type.
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getStringExtra(String name)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_APN_TYPE = PhoneConstants.DATA_APN_TYPE_KEY;
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for an String containing the data APN.
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getStringExtra(String name)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_APN = PhoneConstants.DATA_APN_KEY;
-
-    /**
      * Broadcast intent action for letting the default dialer to know to show voicemail
      * notification.
      *
@@ -5107,7 +5083,11 @@
     @UnsupportedAppUsage
     private IPhoneSubInfo getSubscriberInfo() {
         // get it each time because that process crashes a lot
-        return IPhoneSubInfo.Stub.asInterface(ServiceManager.getService("iphonesubinfo"));
+        return IPhoneSubInfo.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getPhoneSubServiceRegisterer()
+                        .get());
     }
 
     /**
@@ -5320,11 +5300,19 @@
     }
 
     private ITelephonyRegistry getTelephonyRegistry() {
-        return ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry"));
+        return ITelephonyRegistry.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getTelephonyRegistryServiceRegisterer()
+                        .get());
     }
 
     private IOns getIOns() {
-        return IOns.Stub.asInterface(ServiceManager.getService("ions"));
+        return IOns.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getOpportunisticNetworkServiceRegisterer()
+                        .get());
     }
 
     //
@@ -5880,7 +5868,10 @@
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @param p2 P2 parameter (described in ISO 7816-4).
      * @return an IccOpenLogicalChannelResponse object.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) {
         return iccOpenLogicalChannel(getSubId(), AID, p2);
     }
@@ -5911,7 +5902,10 @@
      * @param p2 P2 parameter (described in ISO 7816-4).
      * @return an IccOpenLogicalChannelResponse object.
      * @hide
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     public IccOpenLogicalChannelResponse iccOpenLogicalChannel(int subId, String AID, int p2) {
         try {
             ITelephony telephony = getITelephony();
@@ -5939,7 +5933,10 @@
      *            iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
      * @hide
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @SystemApi
     public boolean iccCloseLogicalChannelBySlot(int slotIndex, int channel) {
@@ -5966,7 +5963,10 @@
      * @param channel is the channel id to be closed as returned by a successful
      *            iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     public boolean iccCloseLogicalChannel(int channel) {
         return iccCloseLogicalChannel(getSubId(), channel);
     }
@@ -5985,7 +5985,10 @@
      *            iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
      * @hide
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     public boolean iccCloseLogicalChannel(int subId, int channel) {
         try {
             ITelephony telephony = getITelephony();
@@ -6021,7 +6024,10 @@
      * @return The APDU response from the ICC card with the status appended at the end, or null if
      * there is an issue connecting to the Telephony service.
      * @hide
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @SystemApi
     @Nullable
@@ -6059,7 +6065,10 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     public String iccTransmitApduLogicalChannel(int channel, int cla,
             int instruction, int p1, int p2, int p3, String data) {
         return iccTransmitApduLogicalChannel(getSubId(), channel, cla,
@@ -6088,7 +6097,10 @@
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
      * @hide
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     public String iccTransmitApduLogicalChannel(int subId, int channel, int cla,
             int instruction, int p1, int p2, int p3, String data) {
         try {
@@ -6124,7 +6136,10 @@
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
      * @hide
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @SystemApi
     @NonNull
@@ -6160,7 +6175,10 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     public String iccTransmitApduBasicChannel(int cla,
             int instruction, int p1, int p2, int p3, String data) {
         return iccTransmitApduBasicChannel(getSubId(), cla,
@@ -6187,7 +6205,10 @@
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
      * @hide
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     public String iccTransmitApduBasicChannel(int subId, int cla,
             int instruction, int p1, int p2, int p3, String data) {
         try {
@@ -6215,7 +6236,10 @@
      * @param p3 P3 value of the APDU command.
      * @param filePath
      * @return The APDU response.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
             String filePath) {
         return iccExchangeSimIO(getSubId(), fileID, command, p1, p2, p3, filePath);
@@ -6237,7 +6261,10 @@
      * @param filePath
      * @return The APDU response.
      * @hide
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     public byte[] iccExchangeSimIO(int subId, int fileID, int command, int p1, int p2,
             int p3, String filePath) {
         try {
@@ -6263,7 +6290,10 @@
      * @return The APDU response from the ICC card in hexadecimal format
      *         with the last 4 bytes being the status word. If the command fails,
      *         returns an empty string.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     public String sendEnvelopeWithStatus(String content) {
         return sendEnvelopeWithStatus(getSubId(), content);
     }
@@ -6283,7 +6313,10 @@
      *         with the last 4 bytes being the status word. If the command fails,
      *         returns an empty string.
      * @hide
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
      */
+    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+    @Deprecated
     public String sendEnvelopeWithStatus(int subId, String content) {
         try {
             ITelephony telephony = getITelephony();
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 96b6db7..a1d40e8 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -16,10 +16,11 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.Nullable;
-import android.content.Context;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -29,7 +30,6 @@
 import android.os.Messenger;
 import android.os.Parcelable;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.util.SparseArray;
 
 import com.android.internal.telephony.ITelephony;
@@ -234,6 +234,9 @@
 
     private ITelephony getITelephony() {
         return ITelephony.Stub.asInterface(
-            ServiceManager.getService(Context.TELEPHONY_SERVICE));
+            TelephonyFrameworkInitializer
+                    .getTelephonyServiceManager()
+                    .getTelephonyServiceRegisterer()
+                    .get());
     }
 }
diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java
index 93ccba1..81a09c6 100644
--- a/telephony/java/android/telephony/UiccAccessRule.java
+++ b/telephony/java/android/telephony/UiccAccessRule.java
@@ -15,6 +15,8 @@
  */
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
diff --git a/telephony/java/android/telephony/VoLteServiceState.java b/telephony/java/android/telephony/VoLteServiceState.java
index 414b999..1214012 100644
--- a/telephony/java/android/telephony/VoLteServiceState.java
+++ b/telephony/java/android/telephony/VoLteServiceState.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import com.android.telephony.Rlog;
+
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Bundle;
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index dbfb6a2..fab1bf2 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -28,7 +28,7 @@
 import android.provider.Telephony.Carriers;
 import android.telephony.Annotation.ApnType;
 import android.telephony.Annotation.NetworkType;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 372bdf1..bff12b6 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -31,7 +31,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.AccessNetworkConstants;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 11dc78a..d33d3f9 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -22,7 +22,7 @@
 import android.annotation.SystemApi;
 import android.net.LinkProperties;
 import android.os.RemoteException;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.telephony.data.DataService.DataServiceProvider;
 
 import java.lang.annotation.Retention;
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index e793979..8220b16 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -28,7 +28,7 @@
 import android.os.RemoteException;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.Annotation.ApnType;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index 1666265..cd3fc95 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -25,7 +25,7 @@
 import android.os.Parcelable;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index 994c49c..e16fffa 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -21,8 +21,8 @@
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.service.euicc.EuiccProfileInfo;
+import android.telephony.TelephonyFrameworkInitializer;
 import android.util.Log;
 
 import com.android.internal.telephony.euicc.IAuthenticateServerCallback;
@@ -148,7 +148,10 @@
 
     private IEuiccCardController getIEuiccCardController() {
         return IEuiccCardController.Stub.asInterface(
-                ServiceManager.getService("euicc_card_controller"));
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getEuiccCardControllerServiceRegisterer()
+                        .get());
     }
 
     /**
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index cb66a96..d5a48df 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -30,7 +30,7 @@
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.TelephonyManager;
 import android.telephony.euicc.EuiccCardManager.ResetOption;
 
@@ -968,6 +968,10 @@
     }
 
     private static IEuiccController getIEuiccController() {
-        return IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller"));
+        return IEuiccController.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getEuiccControllerService()
+                        .get());
     }
 }
diff --git a/telephony/java/android/telephony/ims/ImsConferenceState.java b/telephony/java/android/telephony/ims/ImsConferenceState.java
index 8d2049b..abfee61 100644
--- a/telephony/java/android/telephony/ims/ImsConferenceState.java
+++ b/telephony/java/android/telephony/ims/ImsConferenceState.java
@@ -24,7 +24,7 @@
 import android.os.Parcelable;
 import android.telecom.Call;
 import android.telecom.Connection;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.util.Log;
 
 import java.util.HashMap;
diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java
index dcb9c9d..136a83e 100644
--- a/telephony/java/android/telephony/ims/ImsExternalCallState.java
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java
@@ -24,7 +24,7 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 057d22c..91514e9 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -25,14 +25,13 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.content.Context;
 import android.os.Binder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
@@ -1018,7 +1017,10 @@
 
     private static ITelephony getITelephony() {
         ITelephony binder = ITelephony.Stub.asInterface(
-                ServiceManager.getService(Context.TELEPHONY_SERVICE));
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getTelephonyServiceRegisterer()
+                        .get());
         if (binder == null) {
             throw new RuntimeException("Could not find Telephony Service.");
         }
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index d3fb37f..c962714 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -26,8 +26,8 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.telephony.AccessNetworkConstants;
+import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRcsController;
 import android.telephony.ims.feature.ImsFeature;
@@ -35,6 +35,8 @@
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 
+import com.android.internal.telephony.IIntegerConsumer;
+
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -158,9 +160,20 @@
         if (executor == null) {
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "Register registration callback: IImsRcsController is null");
+            throw new ImsException("Cannot find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
         c.setExecutor(executor);
-        throw new UnsupportedOperationException("registerImsRegistrationCallback is not"
-                + "supported.");
+        try {
+            imsRcsController.registerImsRegistrationCallback(mSubId, c.getBinder());
+        } catch (RemoteException | IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 
     /**{@inheritDoc}*/
@@ -171,8 +184,18 @@
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
         }
-        throw new UnsupportedOperationException("unregisterImsRegistrationCallback is not"
-                + "supported.");
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "Unregister registration callback: IImsRcsController is null");
+            throw new IllegalStateException("Cannot find remote IMS service");
+        }
+
+        try {
+            imsRcsController.unregisterImsRegistrationCallback(mSubId, c.getBinder());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
     }
 
     /**{@inheritDoc}*/
@@ -186,8 +209,23 @@
         if (executor == null) {
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
-        throw new UnsupportedOperationException("getRegistrationState is not"
-                + "supported.");
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "Get registration state error: IImsRcsController is null");
+            throw new IllegalStateException("Cannot find remote IMS service");
+        }
+
+        try {
+            imsRcsController.getImsRcsRegistrationState(mSubId, new IIntegerConsumer.Stub() {
+                @Override
+                public void accept(int result) {
+                    executor.execute(() -> stateCallback.accept(result));
+                }
+            });
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
     }
 
     /**{@inheritDoc}*/
@@ -202,10 +240,25 @@
         if (executor == null) {
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
-        throw new UnsupportedOperationException("getRegistrationTransportType is not"
-                + "supported.");
-    }
 
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "Get registration transport type error: IImsRcsController is null");
+            throw new IllegalStateException("Cannot find remote IMS service");
+        }
+
+        try {
+            imsRcsController.getImsRcsRegistrationTransportType(mSubId,
+                    new IIntegerConsumer.Stub() {
+                        @Override
+                        public void accept(int result) {
+                            executor.execute(() -> transportTypeCallback.accept(result));
+                        }
+                    });
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 
     /**
      * Registers an {@link AvailabilityCallback} with the system, which will provide RCS
@@ -362,7 +415,10 @@
     }
 
     private IImsRcsController getIImsRcsController() {
-        IBinder binder = ServiceManager.getService(Context.TELEPHONY_IMS_SERVICE);
+        IBinder binder = TelephonyFrameworkInitializer
+                .getTelephonyServiceManager()
+                .getTelephonyImsServiceRegisterer()
+                .get();
         return IImsRcsController.Stub.asInterface(binder);
     }
 }
diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
index 6b72859..2d2e638 100644
--- a/telephony/java/android/telephony/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -22,7 +22,7 @@
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index e4d6335..f052180 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -25,14 +25,13 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.WorkerThread;
-import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.aidl.IImsConfigCallback;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsConfigImplBase;
@@ -415,7 +414,11 @@
     }
 
     private static boolean isImsAvailableOnDevice() {
-        IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+        IPackageManager pm = IPackageManager.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getPackageManagerServiceRegisterer()
+                        .get());
         if (pm == null) {
             // For some reason package manger is not available.. This will fail internally anyways,
             // so do not throw error and allow.
@@ -432,7 +435,10 @@
 
     private static ITelephony getITelephony() {
         ITelephony binder = ITelephony.Stub.asInterface(
-                ServiceManager.getService(Context.TELEPHONY_SERVICE));
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getTelephonyServiceRegisterer()
+                        .get());
         if (binder == null) {
             throw new RuntimeException("Could not find Telephony Service.");
         }
diff --git a/telephony/java/android/telephony/ims/RcsControllerCall.java b/telephony/java/android/telephony/ims/RcsControllerCall.java
index ce03c3c..1e93437 100644
--- a/telephony/java/android/telephony/ims/RcsControllerCall.java
+++ b/telephony/java/android/telephony/ims/RcsControllerCall.java
@@ -18,7 +18,7 @@
 
 import android.content.Context;
 import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.aidl.IRcsMessage;
 
 /**
@@ -35,8 +35,11 @@
     }
 
     <R> R call(RcsServiceCall<R> serviceCall) throws RcsMessageStoreException {
-        IRcsMessage iRcsMessage = IRcsMessage.Stub.asInterface(ServiceManager.getService(
-                Context.TELEPHONY_RCS_MESSAGE_SERVICE));
+        IRcsMessage iRcsMessage = IRcsMessage.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getTelephonyRcsMessageServiceRegisterer()
+                        .get());
         if (iRcsMessage == null) {
             throw new RcsMessageStoreException("Could not connect to RCS storage service");
         }
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 75e3f0a..2e3f59a 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -21,12 +21,11 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
-import android.content.Context;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.aidl.IImsRcsController;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
 import android.util.Log;
@@ -365,7 +364,10 @@
     }
 
     private IImsRcsController getIImsRcsController() {
-        IBinder binder = ServiceManager.getService(Context.TELEPHONY_IMS_SERVICE);
+        IBinder binder = TelephonyFrameworkInitializer
+                .getTelephonyServiceManager()
+                .getTelephonyImsServiceRegisterer()
+                .get();
         return IImsRcsController.Stub.asInterface(binder);
     }
 }
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index e81bac0..6f6aa44 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -19,6 +19,9 @@
 import android.net.Uri;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+
+import com.android.internal.telephony.IIntegerConsumer;
 
 /**
  * Interface used to interact with the Telephony IMS.
@@ -26,6 +29,13 @@
  * {@hide}
  */
 interface IImsRcsController {
+    // IMS RCS registration commands
+    void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c);
+    void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c);
+    void getImsRcsRegistrationState(int subId, IIntegerConsumer consumer);
+    void getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer);
+
+    // IMS RCS capability commands
     void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
     void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
     boolean isCapable(int subId, int capability, int radioTech);
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index cfc803c..9116a3b 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -19,7 +19,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.aidl.IImsConfig;
diff --git a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
index 1d6ec2d..8e86ff7 100644
--- a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
+++ b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
@@ -19,7 +19,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.util.SparseIntArray;
 
 import com.android.internal.telephony.cdma.sms.UserData;
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index e75c593..832502c 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -20,7 +20,7 @@
 import android.content.res.Resources;
 import android.sysprop.TelephonyProperties;
 import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.telephony.SmsCbLocation;
 import android.telephony.SmsCbMessage;
 import android.telephony.cdma.CdmaSmsCbProgramData;
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index b5af646..cbf0f5c 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -18,7 +18,7 @@
 
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.telephony.SmsCbCmasInfo;
 import android.telephony.cdma.CdmaSmsCbProgramData;
 import android.telephony.cdma.CdmaSmsCbProgramResults;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 0681dc1..417aafd 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -28,7 +28,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.EncodeException;
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index eed9a86..0dc7401 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -21,7 +21,7 @@
 import android.content.res.Resources.NotFoundException;
 import android.graphics.Bitmap;
 import android.graphics.Color;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet;
diff --git a/telephony/java/com/android/telephony/Rlog.java b/telephony/java/com/android/telephony/Rlog.java
new file mode 100644
index 0000000..9d6c930
--- /dev/null
+++ b/telephony/java/com/android/telephony/Rlog.java
@@ -0,0 +1,154 @@
+/*
+ * 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.telephony;
+
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * A copy of {@link android.telephony.Rlog} to be used within the telephony mainline module.
+ *
+ * @hide
+ */
+public final class Rlog {
+
+    private static final boolean USER_BUILD = TelephonyUtils.IS_USER;
+
+    private Rlog() {
+    }
+
+    private static int log(int priority, String tag, String msg) {
+        return Log.logToRadioBuffer(priority, tag, msg);
+    }
+
+    public static int v(String tag, String msg) {
+        return log(Log.VERBOSE, tag, msg);
+    }
+
+    public static int v(String tag, String msg, Throwable tr) {
+        return log(Log.VERBOSE, tag,
+                msg + '\n' + Log.getStackTraceString(tr));
+    }
+
+    public static int d(String tag, String msg) {
+        return log(Log.DEBUG, tag, msg);
+    }
+
+    public static int d(String tag, String msg, Throwable tr) {
+        return log(Log.DEBUG, tag,
+                msg + '\n' + Log.getStackTraceString(tr));
+    }
+
+    public static int i(String tag, String msg) {
+        return log(Log.INFO, tag, msg);
+    }
+
+    public static int i(String tag, String msg, Throwable tr) {
+        return log(Log.INFO, tag,
+                msg + '\n' + Log.getStackTraceString(tr));
+    }
+
+    public static int w(String tag, String msg) {
+        return log(Log.WARN, tag, msg);
+    }
+
+    public static int w(String tag, String msg, Throwable tr) {
+        return log(Log.WARN, tag,
+                msg + '\n' + Log.getStackTraceString(tr));
+    }
+
+    public static int w(String tag, Throwable tr) {
+        return log(Log.WARN, tag, Log.getStackTraceString(tr));
+    }
+
+    public static int e(String tag, String msg) {
+        return log(Log.ERROR, tag, msg);
+    }
+
+    public static int e(String tag, String msg, Throwable tr) {
+        return log(Log.ERROR, tag,
+                msg + '\n' + Log.getStackTraceString(tr));
+    }
+
+    public static int println(int priority, String tag, String msg) {
+        return log(priority, tag, msg);
+    }
+
+    public static boolean isLoggable(String tag, int level) {
+        return Log.isLoggable(tag, level);
+    }
+
+    /**
+     * Redact personally identifiable information for production users.
+     * @param tag used to identify the source of a log message
+     * @param pii the personally identifiable information we want to apply secure hash on.
+     * @return If tag is loggable in verbose mode or pii is null, return the original input.
+     * otherwise return a secure Hash of input pii
+     */
+    public static String pii(String tag, Object pii) {
+        String val = String.valueOf(pii);
+        if (pii == null || TextUtils.isEmpty(val) || isLoggable(tag, Log.VERBOSE)) {
+            return val;
+        }
+        return "[" + secureHash(val.getBytes()) + "]";
+    }
+
+    /**
+     * Redact personally identifiable information for production users.
+     * @param enablePiiLogging set when caller explicitly want to enable sensitive logging.
+     * @param pii the personally identifiable information we want to apply secure hash on.
+     * @return If enablePiiLogging is set to true or pii is null, return the original input.
+     * otherwise return a secure Hash of input pii
+     */
+    public static String pii(boolean enablePiiLogging, Object pii) {
+        String val = String.valueOf(pii);
+        if (pii == null || TextUtils.isEmpty(val) || enablePiiLogging) {
+            return val;
+        }
+        return "[" + secureHash(val.getBytes()) + "]";
+    }
+
+    /**
+     * Returns a secure hash (using the SHA1 algorithm) of the provided input.
+     *
+     * @return "****" if the build type is user, otherwise the hash
+     * @param input the bytes for which the secure hash should be computed.
+     */
+    private static String secureHash(byte[] input) {
+        // Refrain from logging user personal information in user build.
+        if (USER_BUILD) {
+            return "****";
+        }
+
+        MessageDigest messageDigest;
+
+        try {
+            messageDigest = MessageDigest.getInstance("SHA-1");
+        } catch (NoSuchAlgorithmException e) {
+            return "####";
+        }
+
+        byte[] result = messageDigest.digest(input);
+        return Base64.encodeToString(
+                result, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
+    }
+}
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
index 653282d..1361df3 100644
--- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -320,8 +320,9 @@
                             UserHandle.USER_CURRENT);
                 }
 
-                mAtm.startActivityAndWait(null, null, mLaunchIntent, mimeType,
-                        null, null, 0, mLaunchIntent.getFlags(), null, null,
+                mAtm.startActivityAndWait(null,
+                        getInstrumentation().getContext().getBasePackageName(), mLaunchIntent,
+                        mimeType, null, null, 0, mLaunchIntent.getFlags(), null, null,
                         UserHandle.USER_CURRENT_OR_SELF);
             } catch (RemoteException e) {
                 Log.w(TAG, "Error launching app", e);
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index b4cafe4..656628eb 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -896,37 +896,76 @@
         assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
     }
 
-    /** Test that observers execute correctly for different failure reasons */
+    /** Test that observers execute correctly for failures reasons that go through thresholding. */
     @Test
-    public void testFailureReasons() {
+    public void testNonImmediateFailureReasons() {
         PackageWatchdog watchdog = createWatchdog();
         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
         TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
-        TestObserver observer3 = new TestObserver(OBSERVER_NAME_3);
-        TestObserver observer4 = new TestObserver(OBSERVER_NAME_4);
 
         watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
         watchdog.startObservingHealth(observer2, Arrays.asList(APP_B), SHORT_DURATION);
-        watchdog.startObservingHealth(observer3, Arrays.asList(APP_C), SHORT_DURATION);
-        watchdog.startObservingHealth(observer4, Arrays.asList(APP_D), SHORT_DURATION);
+
+        raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
+                VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+        raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_B,
+                VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
+
+        assertThat(observer1.getLastFailureReason()).isEqualTo(
+                PackageWatchdog.FAILURE_REASON_APP_CRASH);
+        assertThat(observer2.getLastFailureReason()).isEqualTo(
+                PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
+    }
+
+    /** Test that observers execute correctly for failures reasons that skip thresholding. */
+    @Test
+    public void testImmediateFailures() {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+
+        watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
 
         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_B,
                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
-        raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_C,
-                VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH);
-        raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_D,
-                VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
 
-        assertThat(observer1.getLastFailureReason()).isEqualTo(
-                PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
-        assertThat(observer2.getLastFailureReason()).isEqualTo(
-                PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
-        assertThat(observer3.getLastFailureReason()).isEqualTo(
-                PackageWatchdog.FAILURE_REASON_APP_CRASH);
-        assertThat(observer4.getLastFailureReason()).isEqualTo(
-                PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
+        assertThat(observer1.mMitigatedPackages).containsExactly(APP_A, APP_B);
+    }
+
+    /**
+     * Test that a persistent observer will mitigate failures if it wishes to observe a package.
+     */
+    @Test
+    public void testPersistentObserverWatchesPackage() {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver persistentObserver = new TestObserver(OBSERVER_NAME_1);
+        persistentObserver.setPersistent(true);
+        persistentObserver.setMayObservePackages(true);
+
+        watchdog.startObservingHealth(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
+
+        raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
+                VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
+        assertThat(persistentObserver.mHealthCheckFailedPackages).containsExactly(APP_A);
+    }
+
+    /**
+     * Test that a persistent observer will not mitigate failures if it does not wish to observe
+     * a given package.
+     */
+    @Test
+    public void testPersistentObserverDoesNotWatchPackage() {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver persistentObserver = new TestObserver(OBSERVER_NAME_1);
+        persistentObserver.setPersistent(true);
+        persistentObserver.setMayObservePackages(false);
+
+        watchdog.startObservingHealth(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
+
+        raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
+                VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
+        assertThat(persistentObserver.mHealthCheckFailedPackages).isEmpty();
     }
 
     private void adoptShellPermissions(String... permissions) {
@@ -964,7 +1003,12 @@
     /** Trigger package failures above the threshold. */
     private void raiseFatalFailureAndDispatch(PackageWatchdog watchdog,
             List<VersionedPackage> packages, int failureReason) {
-        for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) {
+        long triggerFailureCount = watchdog.getTriggerFailureCount();
+        if (failureReason == PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK
+                || failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+            triggerFailureCount = 1;
+        }
+        for (int i = 0; i < triggerFailureCount; i++) {
             watchdog.onPackageFailure(packages, failureReason);
         }
         mTestLooper.dispatchAll();
@@ -1000,6 +1044,8 @@
         private final String mName;
         private int mImpact;
         private int mLastFailureReason;
+        private boolean mIsPersistent = false;
+        private boolean mMayObservePackages = false;
         final List<String> mHealthCheckFailedPackages = new ArrayList<>();
         final List<String> mMitigatedPackages = new ArrayList<>();
 
@@ -1028,9 +1074,25 @@
             return mName;
         }
 
+        public boolean isPersistent() {
+            return mIsPersistent;
+        }
+
+        public boolean mayObservePackage(String packageName) {
+            return mMayObservePackages;
+        }
+
         public int getLastFailureReason() {
             return mLastFailureReason;
         }
+
+        public void setPersistent(boolean persistent) {
+            mIsPersistent = persistent;
+        }
+
+        public void setMayObservePackages(boolean mayObservePackages) {
+            mMayObservePackages = mayObservePackages;
+        }
     }
 
     private static class TestController extends ExplicitHealthCheckController {
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 40169b8..9e490f7 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -324,6 +324,7 @@
 
     @Test
     public void testNetworkPassedDoesNotRollback_Phase1() throws Exception {
+        // Remove available rollbacks and uninstall NetworkStack on /data/
         RollbackManager rm = RollbackUtils.getRollbackManager();
         String networkStack = getNetworkStackPackageName();
 
@@ -332,6 +333,15 @@
 
         assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
                         networkStack)).isNull();
+
+        // Reduce health check deadline, here unlike the network failed case, we use
+        // a longer deadline because joining a network can take a much longer time for
+        // reasons external to the device than 'not joining'
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+                PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS,
+                Integer.toString(300000), false);
+        // Simulate re-installation of new NetworkStack with rollbacks enabled
+        installNetworkStackPackage();
     }
 
     @Test
@@ -343,6 +353,9 @@
 
     @Test
     public void testNetworkPassedDoesNotRollback_Phase3() throws Exception {
+        // Sleep for > health check deadline. We expect no rollback should happen during sleeping.
+        // If the device reboots for rollback, this device test will fail as well as the host test.
+        Thread.sleep(TimeUnit.SECONDS.toMillis(310));
         RollbackManager rm = RollbackUtils.getRollbackManager();
         assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
                         getNetworkStackPackageName())).isNull();
@@ -380,11 +393,6 @@
     }
 
     @Test
-    public void testRollbackWhitelistedApp_cleanUp() throws Exception {
-        RollbackUtils.getRollbackManager().expireRollbackForPackage(getModuleMetadataPackageName());
-    }
-
-    @Test
     public void testRollbackDataPolicy_Phase1() throws Exception {
         Uninstall.packages(TestApp.A, TestApp.B);
         Install.multi(TestApp.A1, TestApp.B1).commit();
@@ -422,6 +430,16 @@
         assertThat(InstallUtils.getUserDataVersion(TestApp.B)).isEqualTo(1);
     }
 
+    @Test
+    public void testCleanUp() throws Exception {
+        // testNativeWatchdogTriggersRollback will fail if multiple staged sessions are
+        // committed on a device which doesn't support checkpoint. Let's clean up all rollbacks
+        // so there is only one rollback to commit when testing native crashes.
+        RollbackManager rm  = RollbackUtils.getRollbackManager();
+        rm.getAvailableRollbacks().stream().flatMap(info -> info.getPackages().stream())
+                .map(info -> info.getPackageName()).forEach(rm::expireRollbackForPackage);
+    }
+
     private static void runShellCommand(String cmd) {
         ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .executeShellCommand(cmd);
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 4644d8a..91577c2 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
@@ -22,8 +22,8 @@
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
+import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -53,6 +53,11 @@
         getDevice().reboot();
     }
 
+    @After
+    public void tearDown() throws Exception {
+        runPhase("testCleanUp");
+    }
+
     /**
      * Tests watchdog triggered staged rollbacks involving only apks.
      */
@@ -128,21 +133,8 @@
      * Tests passed network health check does not trigger watchdog staged rollbacks.
      */
     @Test
-    @Ignore("b/143514090")
     public void testNetworkPassedDoesNotRollback() throws Exception {
-        // Remove available rollbacks and uninstall NetworkStack on /data/
         runPhase("testNetworkPassedDoesNotRollback_Phase1");
-        // Reduce health check deadline, here unlike the network failed case, we use
-        // a longer deadline because joining a network can take a much longer time for
-        // reasons external to the device than 'not joining'
-        getDevice().executeShellCommand("device_config put rollback "
-                + "watchdog_request_timeout_millis 300000");
-        // Simulate re-installation of new NetworkStack with rollbacks enabled
-        getDevice().executeShellCommand("pm install -r --staged --enable-rollback "
-                + getNetworkStackPath());
-
-        // Sleep to allow writes to disk before reboot
-        Thread.sleep(5000);
         // Reboot device to activate staged package
         getDevice().reboot();
 
@@ -157,8 +149,6 @@
         // on mobile data
         getDevice().waitForDeviceAvailable();
 
-        // Sleep for > health check deadline
-        Thread.sleep(310000);
         // Verify rollback was not executed after health check deadline
         runPhase("testNetworkPassedDoesNotRollback_Phase3");
     }
@@ -180,16 +170,9 @@
      */
     @Test
     public void testRollbackWhitelistedApp() throws Exception {
-        try {
-            runPhase("testRollbackWhitelistedApp_Phase1");
-            getDevice().reboot();
-            runPhase("testRollbackWhitelistedApp_Phase2");
-        } finally {
-            // testNativeWatchdogTriggersRollback will fail if multiple staged sessions are
-            // committed on a device which doesn't support checkpoint. Let's clean up the rollback
-            // so there is only one rollback to commit when testing native crashes.
-            runPhase("testRollbackWhitelistedApp_cleanUp");
-        }
+        runPhase("testRollbackWhitelistedApp_Phase1");
+        getDevice().reboot();
+        runPhase("testRollbackWhitelistedApp_Phase2");
     }
 
     @Test
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index c16a0f4..33d77d2 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -64,15 +64,15 @@
     @Test
     public void testFindIndex() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 5)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
-                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12)
-                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+                .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12);
 
         assertEquals(4, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES,
@@ -94,21 +94,21 @@
     @Test
     public void testFindIndexHinted() {
         final NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
-                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
-                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 1024L, 8L, 0L, 0L, 10)
-                .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
-                .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+                .addEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
-                .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
-                .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+                .addEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
                         DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12);
 
         // verify that we correctly find across regardless of hinting
@@ -143,27 +143,27 @@
         assertEquals(0, stats.size());
         assertEquals(4, stats.internalSize());
 
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                 DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
                 DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5);
 
         assertEquals(4, stats.size());
         assertEquals(4, stats.internalSize());
 
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                 DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11);
-        stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
                 DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11);
 
         assertEquals(9, stats.size());
@@ -193,8 +193,8 @@
     public void testCombineExisting() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 10);
 
-        stats.addValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10);
-        stats.addValues(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
+        stats.addEntry(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10);
+        stats.addEntry(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
         stats.combineValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, -128L, -1L,
                 -128L, -1L, -1);
 
@@ -215,12 +215,12 @@
     @Test
     public void testSubtractIdenticalData() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats result = after.subtract(before);
 
@@ -234,12 +234,12 @@
     @Test
     public void testSubtractIdenticalRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
 
         final NetworkStats result = after.subtract(before);
 
@@ -253,13 +253,13 @@
     @Test
     public void testSubtractNewRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
-                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
+                .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
 
         final NetworkStats result = after.subtract(before);
 
@@ -275,11 +275,11 @@
     @Test
     public void testSubtractMissingRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
-                .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0);
+                .addEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
+                .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0);
 
         final NetworkStats after = new NetworkStats(TEST_START, 1)
-                .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0);
+                .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0);
 
         final NetworkStats result = after.subtract(before);
 
@@ -293,40 +293,40 @@
     @Test
     public void testTotalBytes() throws Exception {
         final NetworkStats iface = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L);
+                .addEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
+                .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L);
         assertEquals(384L, iface.getTotalBytes());
 
         final NetworkStats uidSet = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+                .addEntry(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
         assertEquals(96L, uidSet.getTotalBytes());
 
         final NetworkStats uidTag = new NetworkStats(TEST_START, 6)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
+                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
         assertEquals(64L, uidTag.getTotalBytes());
 
         final NetworkStats uidMetered = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
         assertEquals(96L, uidMetered.getTotalBytes());
 
         final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
         assertEquals(96L, uidRoaming.getTotalBytes());
     }
@@ -343,11 +343,11 @@
     @Test
     public void testGroupedByIfaceAll() throws Exception {
         final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
-                .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
-                .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
+                .addEntry(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 128L, 8L, 0L, 2L, 20L)
-                .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
+                .addEntry(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L);
         final NetworkStats grouped = uidStats.groupedByIface();
 
@@ -361,19 +361,19 @@
     @Test
     public void testGroupedByIface() throws Exception {
         final NetworkStats uidStats = new NetworkStats(TEST_START, 7)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L);
 
         final NetworkStats grouped = uidStats.groupedByIface();
@@ -390,19 +390,19 @@
     @Test
     public void testAddAllValues() {
         final NetworkStats first = new NetworkStats(TEST_START, 5)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+                .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
 
         final NetworkStats second = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+                .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
 
         first.combineAllValues(second);
@@ -421,19 +421,19 @@
     @Test
     public void testGetTotal() {
         final NetworkStats stats = new NetworkStats(TEST_START, 7)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 512L,32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                         DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
 
         assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L);
@@ -459,7 +459,7 @@
         assertEquals(0, after.size());
 
         // Test 1 item stats.
-        before.addValues(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
+        before.addEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
         after = before.clone();
         after.removeUids(new int[0]);
         assertEquals(1, after.size());
@@ -469,12 +469,12 @@
         assertEquals(0, after.size());
 
         // Append remaining test items.
-        before.addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
+        before.addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
+                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
+                .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
         assertEquals(7, before.size());
 
         // Test remove with empty uid list.
@@ -505,12 +505,12 @@
     @Test
     public void testClone() throws Exception {
         final NetworkStats original = new NetworkStats(TEST_START, 5)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
 
         // make clone and mutate original
         final NetworkStats clone = original.clone();
-        original.addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
+        original.addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
 
         assertEquals(3, original.size());
         assertEquals(2, clone.size());
@@ -523,8 +523,8 @@
     public void testAddWhenEmpty() throws Exception {
         final NetworkStats red = new NetworkStats(TEST_START, -1);
         final NetworkStats blue = new NetworkStats(TEST_START, 5)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
+                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
 
         // We're mostly checking that we don't crash
         red.combineAllValues(blue);
@@ -537,39 +537,39 @@
         final String underlyingIface = "wlan0";
         final int testTag1 = 8888;
         NetworkStats delta = new NetworkStats(TEST_START, 17)
-            .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L)
-            .addValues(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
-            .addValues(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L)
-            .addValues(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L)
-            // VPN package also uses some traffic through unprotected network.
-            .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L)
-            .addValues(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
-            // Tag entries
-            .addValues(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L)
-            .addValues(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L)
-            // Irrelevant entries
-            .addValues(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L)
-            // Underlying Iface entries
-            .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L)
-            .addValues(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
-            .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */,
-                    299L /* smaller than sum(tun0) */, 0L)
-            .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
+                .addEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L)
+                .addEntry(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
+                .addEntry(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L)
+                .addEntry(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L)
+                // VPN package also uses some traffic through unprotected network.
+                .addEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L)
+                .addEntry(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
+                // Tag entries
+                .addEntry(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L)
+                .addEntry(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L)
+                // Irrelevant entries
+                .addEntry(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L)
+                // Underlying Iface entries
+                .addEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L)
+                .addEntry(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
+                .addEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */,
+                        299L /* smaller than sum(tun0) */, 0L)
+                .addEntry(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
 
-        delta.migrateTun(tunUid, tunIface, new String[] {underlyingIface});
+        delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
         assertEquals(20, delta.size());
 
         // tunIface and TEST_IFACE entries are not changed.
@@ -634,21 +634,21 @@
         final String tunIface = "tun0";
         final String underlyingIface = "wlan0";
         NetworkStats delta = new NetworkStats(TEST_START, 9)
-            // 2 different apps sent/receive data via tun0.
-            .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L)
-            .addValues(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L)
-            // VPN package resends data through the tunnel (with exaggerated overhead)
-            .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L)
-            // 1 app already has some traffic on the underlying interface, the other doesn't yet
-            .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L)
-            // Traffic through the underlying interface via the vpn app.
-            // This test should redistribute this data correctly.
-            .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                    DEFAULT_NETWORK_NO,  75500L, 37L, 130000L, 70L, 0L);
+                // 2 different apps sent/receive data via tun0.
+                .addEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L)
+                .addEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L)
+                // VPN package resends data through the tunnel (with exaggerated overhead)
+                .addEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L)
+                // 1 app already has some traffic on the underlying interface, the other doesn't yet
+                .addEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L)
+                // Traffic through the underlying interface via the vpn app.
+                // This test should redistribute this data correctly.
+                .addEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                        DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L);
 
         delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
         assertEquals(9, delta.size());
@@ -697,9 +697,9 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addValues(entry1)
-                .addValues(entry2)
-                .addValues(entry3);
+                .addEntry(entry1)
+                .addEntry(entry2)
+                .addEntry(entry3);
 
         stats.filter(UID_ALL, INTERFACES_ALL, TAG_ALL);
         assertEquals(3, stats.size());
@@ -724,9 +724,9 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addValues(entry1)
-                .addValues(entry2)
-                .addValues(entry3);
+                .addEntry(entry1)
+                .addEntry(entry2)
+                .addEntry(entry3);
 
         stats.filter(testUid, INTERFACES_ALL, TAG_ALL);
         assertEquals(2, stats.size());
@@ -755,10 +755,10 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 4)
-                .addValues(entry1)
-                .addValues(entry2)
-                .addValues(entry3)
-                .addValues(entry4);
+                .addEntry(entry1)
+                .addEntry(entry2)
+                .addEntry(entry3)
+                .addEntry(entry4);
 
         stats.filter(UID_ALL, new String[] { testIf1, testIf2 }, TAG_ALL);
         assertEquals(3, stats.size());
@@ -778,8 +778,8 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addValues(entry1)
-                .addValues(entry2);
+                .addEntry(entry1)
+                .addEntry(entry2);
 
         stats.filter(UID_ALL, new String[] { }, TAG_ALL);
         assertEquals(0, stats.size());
@@ -802,9 +802,9 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addValues(entry1)
-                .addValues(entry2)
-                .addValues(entry3);
+                .addEntry(entry1)
+                .addEntry(entry2)
+                .addEntry(entry3);
 
         stats.filter(UID_ALL, INTERFACES_ALL, testTag);
         assertEquals(2, stats.size());
@@ -831,10 +831,10 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 4)
-                .addValues(entry1)
-                .addValues(entry2)
-                .addValues(entry3)
-                .addValues(entry4);
+                .addEntry(entry1)
+                .addEntry(entry2)
+                .addEntry(entry3)
+                .addEntry(entry4);
 
         stats.filterDebugEntries();
 
@@ -891,14 +891,14 @@
                 0 /* operations */);
 
         final NetworkStats statsXt = new NetworkStats(TEST_START, 3)
-                .addValues(appEntry)
-                .addValues(xtRootUidEntry)
-                .addValues(otherEntry);
+                .addEntry(appEntry)
+                .addEntry(xtRootUidEntry)
+                .addEntry(otherEntry);
 
         final NetworkStats statsEbpf = new NetworkStats(TEST_START, 3)
-                .addValues(appEntry)
-                .addValues(ebpfRootUidEntry)
-                .addValues(otherEntry);
+                .addEntry(appEntry)
+                .addEntry(ebpfRootUidEntry)
+                .addEntry(otherEntry);
 
         statsXt.apply464xlatAdjustments(stackedIface, false);
         statsEbpf.apply464xlatAdjustments(stackedIface, true);
@@ -945,8 +945,8 @@
                 0 /* operations */);
 
         NetworkStats stats = new NetworkStats(TEST_START, 2)
-                .addValues(firstEntry)
-                .addValues(secondEntry);
+                .addEntry(firstEntry)
+                .addEntry(secondEntry);
 
         // Empty map: no adjustment
         stats.apply464xlatAdjustments(new ArrayMap<>(), false);
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
index c0f9dc1..f0e5774 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -326,14 +326,14 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
@@ -359,14 +359,14 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
@@ -391,14 +391,14 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
@@ -424,14 +424,14 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
+                .addEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
                         ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
+                .addEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
                         ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 4d42a61..6de068e 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -298,11 +298,11 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 1024L, 8L, 2048L, 16L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
-                .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
+                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
         mService.setUidForeground(UID_RED, false);
         mService.incrementOperationCount(UID_RED, 0xFAAD, 4);
         mService.setUidForeground(UID_RED, true);
@@ -407,9 +407,9 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
-                .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 10);
 
         forcePollAndWaitForIdle();
@@ -429,9 +429,9 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
-                .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
 
         mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
         forcePollAndWaitForIdle();
@@ -443,10 +443,10 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 2176L, 17L, 1536L, 12L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
-                .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
-                .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
+                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
         mService.incrementOperationCount(UID_BLUE, 0xFAAD, 10);
 
         forcePollAndWaitForIdle();
@@ -480,10 +480,10 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
-                .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
-                .addValues(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
+                .addEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
         mService.incrementOperationCount(UID_RED, 0xFAAD, 10);
 
         forcePollAndWaitForIdle();
@@ -501,10 +501,10 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
-                .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
-                .addValues(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
+                .addEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
         final Intent intent = new Intent(ACTION_UID_REMOVED);
         intent.putExtra(EXTRA_UID, UID_BLUE);
         mServiceContext.sendBroadcast(intent);
@@ -536,8 +536,8 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 5);
 
         forcePollAndWaitForIdle();
@@ -552,8 +552,8 @@
         states = new NetworkState[] {buildMobile4gState(TEST_IFACE2)};
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
 
         mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
         forcePollAndWaitForIdle();
@@ -564,10 +564,10 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
-                .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
-                .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .addEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+                .addEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
         mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
 
         forcePollAndWaitForIdle();
@@ -591,9 +591,9 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
-                .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
+                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
         forcePollAndWaitForIdle();
@@ -608,9 +608,9 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
-                .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L));
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
+                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L));
         forcePollAndWaitForIdle();
 
         // first verify entire history present
@@ -654,9 +654,9 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
-                .addValues(entry1)
-                .addValues(entry2)
-                .addValues(entry3));
+                .addEntry(entry1)
+                .addEntry(entry2)
+                .addEntry(entry3));
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
         NetworkStats stats = mService.getDetailedUidStats(INTERFACES_ALL);
@@ -704,11 +704,11 @@
                 .thenReturn(augmentedIfaceFilter);
         when(mStatsFactory.readNetworkStatsDetail(eq(UID_ALL), any(), eq(TAG_ALL)))
                 .thenReturn(new NetworkStats(getElapsedRealtime(), 1)
-                        .addValues(uidStats));
+                        .addEntry(uidStats));
         when(mNetManager.getNetworkStatsTethering(STATS_PER_UID))
                 .thenReturn(new NetworkStats(getElapsedRealtime(), 2)
-                        .addValues(tetheredStats1)
-                        .addValues(tetheredStats2));
+                        .addEntry(tetheredStats1)
+                        .addEntry(tetheredStats2));
 
         NetworkStats stats = mService.getDetailedUidStats(ifaceFilter);
 
@@ -745,8 +745,8 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
         forcePollAndWaitForIdle();
@@ -760,10 +760,10 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
+                .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
         mService.setUidForeground(UID_RED, true);
         mService.incrementOperationCount(UID_RED, 0xFAAD, 1);
 
@@ -804,9 +804,9 @@
         // and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer.
         // We layer them on top by inspecting the iface properties.
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
@@ -843,9 +843,9 @@
         // ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it
         // on top by inspecting the iface properties.
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
                         DEFAULT_NETWORK_YES,  128L, 2L, 128L, 2L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
         forcePollAndWaitForIdle();
 
@@ -885,10 +885,10 @@
 
         // Traffic for UID_RED.
         final NetworkStats uidStats = new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
+                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
         // All tethering traffic, both hardware and software.
         final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
+                .addEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
                         0L);
 
         expectNetworkStatsSummary(ifaceStats, tetherStatsHardware);
diff --git a/tests/utils/testutils/Android.bp b/tests/utils/testutils/Android.bp
index f71be7b..a6625ab 100644
--- a/tests/utils/testutils/Android.bp
+++ b/tests/utils/testutils/Android.bp
@@ -22,6 +22,7 @@
     static_libs: [
         "junit",
         "hamcrest-library",
+        "androidx.test.runner",
     ],
 
     libs: [
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java b/tests/utils/testutils/java/com/android/server/accessibility/test/MessageCapturingHandler.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java
rename to tests/utils/testutils/java/com/android/server/accessibility/test/MessageCapturingHandler.java
index e2b517f..bce2ab5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java
+++ b/tests/utils/testutils/java/com/android/server/accessibility/test/MessageCapturingHandler.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.accessibility;
+package com.android.server.accessibility.test;
 
 import android.os.Handler;
 import android.os.Looper;
@@ -31,7 +31,7 @@
  * at their target.
  */
 public class MessageCapturingHandler extends Handler {
-    List<Pair<Message, Long>> timedMessages = new ArrayList<>();
+    public List<Pair<Message, Long>> timedMessages = new ArrayList<>();
 
     Handler.Callback mCallback;
 
diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp
index 7f0872c..7fa47f6 100644
--- a/tools/stats_log_api_gen/java_writer.cpp
+++ b/tools/stats_log_api_gen/java_writer.cpp
@@ -85,8 +85,9 @@
         string indent("");
         if (supportQ) {
             // TODO(b/146235828): Use just SDK_INT check once it is incremented from Q.
-            fprintf(out, "        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q ||\n");
-            fprintf(out, "                Build.VERSION.CODENAME.equals(\"R\")) {\n");
+            fprintf(out, "        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q\n");
+            fprintf(out, "                || (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q\n");
+            fprintf(out, "                    && Build.VERSION.PREVIEW_SDK_INT > 0)) {\n");
             indent = "    ";
         }
 
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 634f674..09d5386 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -106,8 +106,10 @@
     name: "framework-wifi-test-defaults",
     sdk_version: "core_platform", // tests can use @CorePlatformApi's
     libs: [
+        // order matters: classes in framework-wifi are resolved before framework, meaning
+        // @hide APIs in framework-wifi are resolved before @SystemApi stubs in framework
         "framework-wifi",
-        "framework-minus-apex",
+        "framework",
 
         // if sdk_version="" this gets automatically included, but here we need to add manually.
         "framework-res",
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1678d5a..71942f0 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -90,6 +90,8 @@
 
     void allowAutojoin(int netId, boolean choice);
 
+    void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin);
+
     boolean startScan(String packageName, String featureId);
 
     List<ScanResult> getScanResults(String callingPackage, String callingFeatureId);
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index a379c75..f4c5b91 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -253,9 +253,12 @@
         /** LEAP/Network EAP (only used with LEAP) */
         public static final int LEAP = 2;
 
+        /** SAE (Used only for WPA3-Personal) */
+        public static final int SAE = 3;
+
         public static final String varName = "auth_alg";
 
-        public static final String[] strings = { "OPEN", "SHARED", "LEAP" };
+        public static final String[] strings = { "OPEN", "SHARED", "LEAP", "SAE" };
     }
 
     /**
@@ -468,10 +471,13 @@
                 break;
             case SECURITY_TYPE_SAE:
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
+                allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+                allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
                 requirePMF = true;
                 break;
             case SECURITY_TYPE_EAP_SUITE_B:
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
+                allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
                 allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
                 allowedGroupManagementCiphers.set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
                 // Note: allowedSuiteBCiphers bitset will be set by the service once the
@@ -480,6 +486,8 @@
                 break;
             case SECURITY_TYPE_OWE:
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
+                allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+                allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
                 requirePMF = true;
                 break;
             case SECURITY_TYPE_WAPI_PSK:
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 378c67b..e870481 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -545,15 +545,22 @@
     public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
 
     /**
-     * The look up key for an int that indicates why softAP started failed
-     * currently support general and no_channel
-     * @see #SAP_START_FAILURE_GENERAL
-     * @see #SAP_START_FAILURE_NO_CHANNEL
-     * @see #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION
+     * An extra containing the int error code for Soft AP start failure.
+     * Can be obtained from the {@link #WIFI_AP_STATE_CHANGED_ACTION} using
+     * {@link android.content.Intent#getIntExtra}.
+     * This extra will only be attached if {@link #EXTRA_WIFI_AP_STATE} is
+     * attached and is equal to {@link #WIFI_AP_STATE_FAILED}.
+     *
+     * The error code will be one of:
+     * {@link #SAP_START_FAILURE_GENERAL},
+     * {@link #SAP_START_FAILURE_NO_CHANNEL},
+     * {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}
      *
      * @hide
      */
-    public static final String EXTRA_WIFI_AP_FAILURE_REASON = "wifi_ap_error_code";
+    @SystemApi
+    public static final String EXTRA_WIFI_AP_FAILURE_REASON =
+            "android.net.wifi.extra.WIFI_AP_FAILURE_REASON";
     /**
      * The previous Wi-Fi state.
      *
@@ -4103,6 +4110,23 @@
     }
 
     /**
+     * Configure auto-join settings for a Passpoint profile.
+     *
+     * @param fqdn the FQDN (fully qualified domain name) of the passpoint profile.
+     * @param enableAutoJoin true to enable autojoin, false to disable autojoin.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void allowAutojoinPasspoint(@NonNull String fqdn, boolean enableAutoJoin) {
+        try {
+            mService.allowAutojoinPasspoint(fqdn, enableAutoJoin);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Disable an ephemeral network.
      *
      * @param ssid in the format of WifiConfiguration's SSID.
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 5befb54..1822e84 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -17,6 +17,7 @@
 package android.net.wifi.hotspot2;
 
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.net.wifi.hotspot2.pps.Credential;
 import android.net.wifi.hotspot2.pps.HomeSp;
 import android.net.wifi.hotspot2.pps.Policy;
@@ -423,6 +424,41 @@
     }
 
     /**
+     * The auto-join configuration specifies whether or not the Passpoint Configuration is
+     * considered for auto-connection. If true then yes, if false then it isn't considered as part
+     * of auto-connection - but can still be manually connected to.
+     */
+    private boolean mIsAutoJoinEnabled = true;
+
+    /**
+     * Configures the auto-association status of this Passpoint configuration. A value of true
+     * indicates that the configuration will be considered for auto-connection, a value of false
+     * indicates that only manual connection will work - the framework will not auto-associate to
+     * this Passpoint network.
+     *
+     * @param autoJoinEnabled true to be considered for framework auto-connection, false otherwise.
+     * @hide
+     */
+    public void setAutoJoinEnabled(boolean autoJoinEnabled) {
+        mIsAutoJoinEnabled = autoJoinEnabled;
+    }
+
+    /**
+     * Indicates whether the Passpoint configuration may be auto-connected to by the framework. A
+     * value of true indicates that auto-connection can happen, a value of false indicates that it
+     * cannot. However, even when auto-connection is not possible manual connection by the user is
+     * possible.
+     *
+     * @return the auto-join configuration: true for auto-connection (or join) enabled, false
+     * otherwise.
+     * @hide
+     */
+    @SystemApi
+    public boolean isAutoJoinEnabled() {
+        return mIsAutoJoinEnabled;
+    }
+
+    /**
      * Constructor for creating PasspointConfiguration with default values.
      */
     public PasspointConfiguration() {}
@@ -464,6 +500,7 @@
         mServiceFriendlyNames = source.mServiceFriendlyNames;
         mAaaServerTrustedNames = source.mAaaServerTrustedNames;
         mCarrierId = source.mCarrierId;
+        mIsAutoJoinEnabled = source.mIsAutoJoinEnabled;
     }
 
     @Override
@@ -493,6 +530,7 @@
                 (HashMap<String, String>) mServiceFriendlyNames);
         dest.writeBundle(bundle);
         dest.writeInt(mCarrierId);
+        dest.writeBoolean(mIsAutoJoinEnabled);
     }
 
     @Override
@@ -523,6 +561,7 @@
                 && mUsageLimitDataLimit == that.mUsageLimitDataLimit
                 && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes
                 && mCarrierId == that.mCarrierId
+                && mIsAutoJoinEnabled == that.mIsAutoJoinEnabled
                 && (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null
                 : mServiceFriendlyNames.equals(that.mServiceFriendlyNames));
     }
@@ -533,7 +572,7 @@
                 mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis,
                 mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes,
                 mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
-                mServiceFriendlyNames, mCarrierId);
+                mServiceFriendlyNames, mCarrierId, mIsAutoJoinEnabled);
     }
 
     @Override
@@ -587,6 +626,7 @@
             builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames);
         }
         builder.append("CarrierId:" + mCarrierId);
+        builder.append("IsAutoJoinEnabled:" + mIsAutoJoinEnabled);
         return builder.toString();
     }
 
@@ -692,6 +732,7 @@
                         "serviceFriendlyNames");
                 config.setServiceFriendlyNames(friendlyNamesMap);
                 config.mCarrierId = in.readInt();
+                config.mIsAutoJoinEnabled = in.readBoolean();
                 return config;
             }
 
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index d58083c..d91efbc 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -182,6 +182,11 @@
     }
 
     @Override
+    public void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public boolean startScan(String packageName, String featureId) {
         throw new UnsupportedOperationException();
     }
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 909cfef..8689a38 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -16,6 +16,10 @@
 
 package android.net.wifi;
 
+import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B;
+import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_OWE;
+import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_SAE;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -328,4 +332,57 @@
             assertNotNull(NetworkSelectionStatus.DISABLE_REASON_INFOS.get(i));
         }
     }
+
+    /**
+     * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the
+     * {@link WifiConfiguration} object correctly for SAE security type.
+     * @throws Exception
+     */
+    @Test
+    public void testSetSecurityParamsForSae() throws Exception {
+        WifiConfiguration config = new WifiConfiguration();
+
+        config.setSecurityParams(SECURITY_TYPE_SAE);
+
+        assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE));
+        assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP));
+        assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP));
+        assertTrue(config.requirePMF);
+    }
+
+    /**
+     * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the
+     * {@link WifiConfiguration} object correctly for OWE security type.
+     * @throws Exception
+     */
+    @Test
+    public void testSetSecurityParamsForOwe() throws Exception {
+        WifiConfiguration config = new WifiConfiguration();
+
+        config.setSecurityParams(SECURITY_TYPE_OWE);
+
+        assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE));
+        assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP));
+        assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP));
+        assertTrue(config.requirePMF);
+    }
+
+    /**
+     * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the
+     * {@link WifiConfiguration} object correctly for Suite-B security type.
+     * @throws Exception
+     */
+    @Test
+    public void testSetSecurityParamsForSuiteB() throws Exception {
+        WifiConfiguration config = new WifiConfiguration();
+
+        config.setSecurityParams(SECURITY_TYPE_EAP_SUITE_B);
+
+        assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+        assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.GCMP_256));
+        assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256));
+        assertTrue(config.allowedGroupManagementCiphers
+                .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+        assertTrue(config.requirePMF);
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index f9bd31d..5ac50a0 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1672,10 +1672,22 @@
     @Test
     public void testAllowAutojoin() throws Exception {
         mWifiManager.allowAutojoin(1, true);
-        verify(mWifiService).allowAutojoin(eq(1), eq(true));
+        verify(mWifiService).allowAutojoin(1, true);
     }
 
     /**
+     * Test behavior of {@link WifiManager#allowAutojoinPasspoint(String, boolean)}
+     * @throws Exception
+     */
+    @Test
+    public void testAllowAutojoinPasspoint() throws Exception {
+        final String fqdn = "FullyQualifiedDomainName";
+        mWifiManager.allowAutojoinPasspoint(fqdn, true);
+        verify(mWifiService).allowAutojoinPasspoint(fqdn, true);
+    }
+
+
+    /**
      * Test behavior of {@link WifiManager#disconnect()}
      */
     @Test
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index f501b16..94054fd 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -171,6 +171,7 @@
 
         assertFalse(config.validate());
         assertFalse(config.validateForR2());
+        assertTrue(config.isAutoJoinEnabled());
     }
 
     /**