Merge changes I6b2f5e01,Ice1b2a7b,Ib75f0162,I70b005a4,I8314a32b

* changes:
  Migrate pullBluetoothActivityInfo
  Migrate pullBluetoothBytesTransfer
  Migrate pullMobileBytesTransferBackground
  Migrate pullMobileBytesTransfer
  Migrate pullWifiBytesTransferBackground
diff --git a/Android.bp b/Android.bp
index 8c85631..4e2b156 100644
--- a/Android.bp
+++ b/Android.bp
@@ -226,6 +226,7 @@
         ":framework-wifi-non-updatable-sources",
         ":PacProcessor-aidl-sources",
         ":ProxyHandler-aidl-sources",
+        ":net-utils-framework-common-srcs",
 
         // AIDL from frameworks/base/native/
         ":platform-compat-native-aidl",
@@ -348,7 +349,6 @@
         "android.hardware.vibrator-V1.1-java",
         "android.hardware.vibrator-V1.2-java",
         "android.hardware.vibrator-V1.3-java",
-        "android.hardware.wifi-V1.0-java-constants",
         "devicepolicyprotosnano",
 
         "com.android.sysprop.apex",
@@ -506,7 +506,10 @@
     defaults: ["framework-defaults"],
     srcs: [":framework-all-sources"],
     installable: false,
-    static_libs: ["exoplayer2-core"],
+    static_libs: [
+        "exoplayer2-core",
+        "android.hardware.wifi-V1.0-java-constants",
+     ],
     apex_available: ["//apex_available:platform"],
 }
 
@@ -604,17 +607,22 @@
 filegroup {
     name: "framework-annotations",
     srcs: [
+        "core/java/android/annotation/CallbackExecutor.java",
+        "core/java/android/annotation/CheckResult.java",
         "core/java/android/annotation/IntDef.java",
         "core/java/android/annotation/IntRange.java",
         "core/java/android/annotation/NonNull.java",
         "core/java/android/annotation/Nullable.java",
         "core/java/android/annotation/RequiresPermission.java",
         "core/java/android/annotation/SdkConstant.java",
+        "core/java/android/annotation/StringDef.java",
         "core/java/android/annotation/SystemApi.java",
+        "core/java/android/annotation/SystemService.java",
         "core/java/android/annotation/TestApi.java",
         "core/java/android/annotation/UnsupportedAppUsage.java",
         "core/java/com/android/internal/annotations/GuardedBy.java",
         "core/java/com/android/internal/annotations/VisibleForTesting.java",
+        "core/java/com/android/internal/annotations/Immutable.java",
     ],
 }
 
@@ -629,6 +637,9 @@
     visibility: ["//frameworks/opt/net/ike"],
     srcs: [
         "core/java/android/net/annotations/PolicyDirection.java",
+        "core/java/com/android/internal/util/IState.java",
+        "core/java/com/android/internal/util/State.java",
+        "core/java/com/android/internal/util/StateMachine.java",
         "telephony/java/android/telephony/Annotation.java",
     ],
 }
@@ -1148,13 +1159,36 @@
     ],
 }
 
+// utility classes statically linked into framework-wifi and dynamically linked
+// into wifi-service
+java_library {
+    name: "framework-wifi-util-lib",
+    sdk_version: "core_current",
+    srcs: [
+        "core/java/android/content/pm/BaseParceledListSlice.java",
+        "core/java/android/content/pm/ParceledListSlice.java",
+        "core/java/android/net/shared/Inet4AddressUtils.java",
+        "core/java/android/os/HandlerExecutor.java",
+        "core/java/com/android/internal/util/AsyncChannel.java",
+        "core/java/com/android/internal/util/AsyncService.java",
+        "core/java/com/android/internal/util/Protocol.java",
+        "core/java/com/android/internal/util/Preconditions.java",
+        "telephony/java/android/telephony/Annotation.java",
+    ],
+    libs: [
+        "framework-annotations-lib",
+        "unsupportedappusage",
+        "android_system_stubs_current",
+    ],
+    visibility: ["//frameworks/base/wifi"],
+}
+
+// utility classes statically linked into wifi-service
 filegroup {
     name: "framework-wifi-service-shared-srcs",
     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",
         "core/java/android/util/Rational.java",
@@ -1162,11 +1196,9 @@
         "core/java/com/android/internal/util/HexDump.java",
         "core/java/com/android/internal/util/IState.java",
         "core/java/com/android/internal/util/MessageUtils.java",
-        "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/WakeupMessage.java",
-        "core/java/com/android/internal/util/XmlUtils.java",
     ],
 }
 
diff --git a/StubLibraries.bp b/StubLibraries.bp
index d195047..baa3c61 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -41,10 +41,9 @@
 ]
 
 stubs_defaults {
-    name: "metalava-api-stubs-default",
+    name: "metalava-non-updatable-api-stubs-default",
     srcs: [
         ":framework-non-updatable-sources",
-        ":framework-updatable-sources",
         "core/java/**/*.logtags",
         ":opt-telephony-srcs",
         ":opt-net-voip-srcs",
@@ -64,14 +63,23 @@
         "sdk-dir",
         "api-versions-jars-dir",
     ],
-    sdk_version: "core_platform",
     filter_packages: packages_to_document,
 }
 
+stubs_defaults {
+    name: "metalava-api-stubs-default",
+    defaults: ["metalava-non-updatable-api-stubs-default"],
+    srcs: [":framework-updatable-sources"],
+    sdk_version: "core_platform",
+}
+
 /////////////////////////////////////////////////////////////////////
 // *-api-stubs-docs modules providing source files for the stub libraries
 /////////////////////////////////////////////////////////////////////
 
+// api-stubs-docs, system-api-stubs-docs, and test-api-stubs-docs have APIs
+// from the non-updatable part of the platform as well as from the updatable
+// modules
 droidstubs {
     name: "api-stubs-docs",
     defaults: ["metalava-api-stubs-default"],
@@ -112,7 +120,10 @@
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
-    args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\)",
+    args: metalava_framework_docs_args +
+        " --show-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+        "process=android.annotation.SystemApi.Process.ALL\\)",
     check_api: {
         current: {
             api_file: "api/system-current.txt",
@@ -155,6 +166,111 @@
 }
 
 /////////////////////////////////////////////////////////////////////
+// Following droidstubs modules are for extra APIs for modules.
+// The framework currently have two more API surfaces for modules:
+// @SystemApi(client=MODULE_APPS) and @SystemApi(client=MODULE_LIBRARIES)
+/////////////////////////////////////////////////////////////////////
+
+// TODO(b/146727827) remove the *-api modules when we can teach metalava
+// about the relationship among the API surfaces. Currently, these modules are only to generate
+// the API signature files and ensure that the APIs evolve in a backwards compatible manner.
+// They however are NOT used for building the API stub.
+droidstubs {
+    name: "module-app-api",
+    defaults: ["metalava-non-updatable-api-stubs-default"],
+    libs: ["framework-all"],
+    arg_files: ["core/res/AndroidManifest.xml"],
+    args: metalava_framework_docs_args +
+        " --show-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.MODULE_APPS," +
+        "process=android.annotation.SystemApi.Process.ALL\\)",
+    check_api: {
+        current: {
+            api_file: "api/module-app-current.txt",
+            removed_api_file: "api/module-app-removed.txt",
+        },
+        // TODO(b/147559833) enable the compatibility check against the last release API
+        // and the API lint
+        //last_released: {
+        //    api_file: ":last-released-module-app-api",
+        //    removed_api_file: "api/module-app-removed.txt",
+        //    baseline_file: ":module-app-api-incompatibilities-with-last-released"
+        //},
+        //api_lint: {
+        //    enabled: true,
+        //    new_since: ":last-released-module-app-api",
+        //    baseline_file: "api/module-app-lint-baseline.txt",
+        //},
+    },
+    //jdiff_enabled: true,
+}
+
+droidstubs {
+    name: "module-lib-api",
+    defaults: ["metalava-non-updatable-api-stubs-default"],
+    libs: ["framework-all"],
+    arg_files: ["core/res/AndroidManifest.xml"],
+    args: metalava_framework_docs_args +
+        " --show-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
+        "process=android.annotation.SystemApi.Process.ALL\\)",
+    check_api: {
+        current: {
+            api_file: "api/module-lib-current.txt",
+            removed_api_file: "api/module-lib-removed.txt",
+        },
+        // TODO(b/147559833) enable the compatibility check against the last release API
+        // and the API lint
+        //last_released: {
+        //    api_file: ":last-released-module-lib-api",
+        //    removed_api_file: "api/module-lib-removed.txt",
+        //    baseline_file: ":module-lib-api-incompatibilities-with-last-released"
+        //},
+        //api_lint: {
+        //    enabled: true,
+        //    new_since: ":last-released-module-lib-api",
+        //    baseline_file: "api/module-lib-lint-baseline.txt",
+        //},
+    },
+    //jdiff_enabled: true,
+}
+
+// The following two droidstubs modules generate source files for the API stub libraries for
+// modules. Note that they not only include their own APIs but also other APIs that have
+// narrower scope. For example, module-lib-api-stubs-docs includes all @SystemApis not just
+// the ones with 'client=MODULE_LIBRARIES'.
+droidstubs {
+    name: "module-app-api-stubs-docs",
+    defaults: ["metalava-non-updatable-api-stubs-default"],
+    libs: ["framework-all"],
+    arg_files: ["core/res/AndroidManifest.xml"],
+    args: metalava_framework_docs_args +
+        " --show-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+        "process=android.annotation.SystemApi.Process.ALL\\)" +
+        " --show-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.MODULE_APPS," +
+        "process=android.annotation.SystemApi.Process.ALL\\)",
+}
+
+droidstubs {
+    name: "module-lib-api-stubs-docs",
+    defaults: ["metalava-non-updatable-api-stubs-default"],
+    libs: ["framework-all"],
+    arg_files: ["core/res/AndroidManifest.xml"],
+    args: metalava_framework_docs_args +
+        " --show-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+        "process=android.annotation.SystemApi.Process.ALL\\)" +
+        " --show-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.MODULE_APPS," +
+        "process=android.annotation.SystemApi.Process.ALL\\)" +
+        " --show-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
+        "process=android.annotation.SystemApi.Process.ALL\\)",
+}
+
+/////////////////////////////////////////////////////////////////////
 // android_*_stubs_current modules are the stubs libraries compiled
 // from *-api-stubs-docs
 /////////////////////////////////////////////////////////////////////
@@ -169,7 +285,6 @@
     java_resources: [
         ":notices-for-framework-stubs",
     ],
-    sdk_version: "core_current",
     system_modules: "none",
     java_version: "1.8",
     compile_dex: true,
@@ -187,6 +302,7 @@
         "private-stub-annotations-jar",
     ],
     defaults: ["framework-stubs-default"],
+    sdk_version: "core_current",
 }
 
 java_library_static {
@@ -201,6 +317,7 @@
         "private-stub-annotations-jar",
     ],
     defaults: ["framework-stubs-default"],
+    sdk_version: "core_current",
 }
 
 java_library_static {
@@ -215,6 +332,37 @@
         "private-stub-annotations-jar",
     ],
     defaults: ["framework-stubs-default"],
+    sdk_version: "core_current",
+}
+
+java_library_static {
+    name: "framework_module_app_stubs_current",
+    srcs: [
+        ":module-app-api-stubs-docs",
+    ],
+    libs: [
+        "stub-annotations",
+        "framework-all",
+    ],
+    static_libs: [
+        "private-stub-annotations-jar",
+    ],
+    defaults: ["framework-stubs-default"],
+}
+
+java_library_static {
+    name: "framework_module_lib_stubs_current",
+    srcs: [
+        ":module-lib-api-stubs-docs",
+    ],
+    libs: [
+        "stub-annotations",
+        "framework-all",
+    ],
+    static_libs: [
+        "private-stub-annotations-jar",
+    ],
+    defaults: ["framework-stubs-default"],
 }
 
 /////////////////////////////////////////////////////////////////////
diff --git a/apex/Android.bp b/apex/Android.bp
index 56f7db2..abebfa3 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -37,6 +37,36 @@
 
 stubs_defaults {
     name: "framework-module-stubs-defaults-systemapi",
-    args: mainline_stubs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) ",
+    args: mainline_stubs_args +
+    " --show-annotation android.annotation.SystemApi\\(" +
+    "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+    "process=android.annotation.SystemApi.Process.ALL\\) ",
+    installable: false,
+}
+
+stubs_defaults {
+    name: "framework-module-stubs-defaults-module_apps_api",
+    args: mainline_stubs_args +
+    " --show-annotation android.annotation.SystemApi\\(" +
+    "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+    "process=android.annotation.SystemApi.Process.ALL\\) " +
+    " --show-annotation android.annotation.SystemApi\\(" +
+    "client=android.annotation.SystemApi.Client.MODULE_APPS," +
+    "process=android.annotation.SystemApi.Process.ALL\\) ",
+    installable: false,
+}
+
+stubs_defaults {
+    name: "framework-module-stubs-defaults-module_libs_api",
+    args: mainline_stubs_args +
+    " --show-annotation android.annotation.SystemApi\\(" +
+    "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+    "process=android.annotation.SystemApi.Process.ALL\\) " +
+    " --show-annotation android.annotation.SystemApi\\(" +
+    "client=android.annotation.SystemApi.Client.MODULE_APPS," +
+    "process=android.annotation.SystemApi.Process.ALL\\) " +
+    " --show-annotation android.annotation.SystemApi\\(" +
+    "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
+    "process=android.annotation.SystemApi.Process.ALL\\) ",
     installable: false,
 }
diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp
index 4ebafce8..8aed5d0 100644
--- a/apex/appsearch/service/Android.bp
+++ b/apex/appsearch/service/Android.bp
@@ -23,5 +23,6 @@
   ],
   static_libs: [
     "icing-java-proto-lite",
-  ]
+  ],
+  apex_available: [ "com.android.appsearch" ],
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 3f58c72..f8b2f32 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -761,6 +761,7 @@
         @Override
         public void onTrigger(TriggerEvent event) {
             synchronized (DeviceIdleController.this) {
+                // One_shot sensors (which call onTrigger) are unregistered when onTrigger is called
                 active = false;
                 motionLocked();
             }
@@ -769,6 +770,9 @@
         @Override
         public void onSensorChanged(SensorEvent event) {
             synchronized (DeviceIdleController.this) {
+                // Since one_shot sensors are unregistered when onTrigger is called, unregister
+                // listeners here so that the MotionListener is in a consistent state when it calls
+                // out to motionLocked.
                 mSensorManager.unregisterListener(this, mMotionSensor);
                 active = false;
                 motionLocked();
diff --git a/apex/statsd/service/Android.bp b/apex/statsd/service/Android.bp
index f3a8989..9103848 100644
--- a/apex/statsd/service/Android.bp
+++ b/apex/statsd/service/Android.bp
@@ -13,4 +13,8 @@
         "framework-minus-apex",
         "services.core",
     ],
+    apex_available: [
+        "com.android.os.statsd",
+        "test_com.android.os.statsd",
+    ],
 }
diff --git a/api/current.txt b/api/current.txt
index 42318fe..504f055 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2925,6 +2925,7 @@
     method public int getShowMode();
     method public boolean removeOnShowModeChangedListener(@NonNull android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener);
     method public boolean setShowMode(int);
+    method public boolean switchToInputMethod(@NonNull String);
   }
 
   public static interface AccessibilityService.SoftKeyboardController.OnShowModeChangedListener {
@@ -6763,6 +6764,7 @@
     method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String);
     method public CharSequence getDeviceOwnerLockScreenInfo();
     method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName);
+    method @Nullable public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName);
     method @Nullable public String getGlobalPrivateDnsHost(@NonNull android.content.ComponentName);
     method public int getGlobalPrivateDnsMode(@NonNull android.content.ComponentName);
     method @NonNull public java.util.List<byte[]> getInstalledCaCerts(@Nullable android.content.ComponentName);
@@ -6881,6 +6883,7 @@
     method public void setDelegatedScopes(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.List<java.lang.String>);
     method public void setDeviceOwnerLockScreenInfo(@NonNull android.content.ComponentName, CharSequence);
     method public void setEndUserSessionMessage(@NonNull android.content.ComponentName, @Nullable CharSequence);
+    method public void setFactoryResetProtectionPolicy(@NonNull android.content.ComponentName, @Nullable android.app.admin.FactoryResetProtectionPolicy);
     method public int setGlobalPrivateDnsModeOpportunistic(@NonNull android.content.ComponentName);
     method @WorkerThread public int setGlobalPrivateDnsModeSpecifiedHost(@NonNull android.content.ComponentName, @NonNull String);
     method public void setGlobalSetting(@NonNull android.content.ComponentName, String, String);
@@ -7115,6 +7118,21 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DnsEvent> CREATOR;
   }
 
+  public final class FactoryResetProtectionPolicy implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.List<java.lang.String> getFactoryResetProtectionAccounts();
+    method public boolean isFactoryResetProtectionDisabled();
+    method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FactoryResetProtectionPolicy> CREATOR;
+  }
+
+  public static class FactoryResetProtectionPolicy.Builder {
+    ctor public FactoryResetProtectionPolicy.Builder();
+    method @NonNull public android.app.admin.FactoryResetProtectionPolicy build();
+    method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionAccounts(@NonNull java.util.List<java.lang.String>);
+    method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionDisabled(boolean);
+  }
+
   public class FreezePeriod {
     ctor public FreezePeriod(java.time.MonthDay, java.time.MonthDay);
     method public java.time.MonthDay getEnd();
@@ -10008,6 +10026,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
@@ -17061,13 +17080,13 @@
     method public abstract void close();
     method @NonNull public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
     method @NonNull public android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int, java.util.Set<java.lang.String>) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void createCaptureSession(@NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method @Deprecated public abstract void createCaptureSession(@NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public void createCaptureSession(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void createConstrainedHighSpeedCaptureSession(@NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method @Deprecated public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method @Deprecated public abstract void createConstrainedHighSpeedCaptureSession(@NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method @NonNull public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(@NonNull android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void createReprocessableCaptureSession(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void createReprocessableCaptureSessionByConfigurations(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method @Deprecated public abstract void createReprocessableCaptureSession(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method @Deprecated public abstract void createReprocessableCaptureSessionByConfigurations(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public int getCameraAudioRestriction() throws android.hardware.camera2.CameraAccessException;
     method @NonNull public abstract String getId();
     method public boolean isSessionConfigurationSupported(@NonNull android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
@@ -23663,6 +23682,7 @@
     field public static final int TYPE_BUILTIN_EARPIECE = 1; // 0x1
     field public static final int TYPE_BUILTIN_MIC = 15; // 0xf
     field public static final int TYPE_BUILTIN_SPEAKER = 2; // 0x2
+    field public static final int TYPE_BUILTIN_SPEAKER_SAFE = 24; // 0x18
     field public static final int TYPE_BUS = 21; // 0x15
     field public static final int TYPE_DOCK = 13; // 0xd
     field public static final int TYPE_FM = 14; // 0xe
@@ -26296,6 +26316,59 @@
     field public static final int SURFACE = 2; // 0x2
   }
 
+  public final class MediaRoute2Info implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getConnectionState();
+    method @Nullable public CharSequence getDescription();
+    method public int getDeviceType();
+    method @Nullable public android.os.Bundle getExtras();
+    method @NonNull public java.util.List<java.lang.String> getFeatures();
+    method @Nullable public android.net.Uri getIconUri();
+    method @NonNull public String getId();
+    method @NonNull public CharSequence getName();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
+    method public boolean hasAnyFeatures(@NonNull java.util.Collection<java.lang.String>);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
+    field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
+    field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.MediaRoute2Info> CREATOR;
+    field public static final int DEVICE_TYPE_BLUETOOTH = 3; // 0x3
+    field public static final int DEVICE_TYPE_REMOTE_SPEAKER = 2; // 0x2
+    field public static final int DEVICE_TYPE_REMOTE_TV = 1; // 0x1
+    field public static final int DEVICE_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
+    field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1
+  }
+
+  public static final class MediaRoute2Info.Builder {
+    ctor public MediaRoute2Info.Builder(@NonNull String, @NonNull CharSequence);
+    ctor public MediaRoute2Info.Builder(@NonNull android.media.MediaRoute2Info);
+    method @NonNull public android.media.MediaRoute2Info.Builder addFeature(@NonNull String);
+    method @NonNull public android.media.MediaRoute2Info.Builder addFeatures(@NonNull java.util.Collection<java.lang.String>);
+    method @NonNull public android.media.MediaRoute2Info build();
+    method @NonNull public android.media.MediaRoute2Info.Builder clearFeatures();
+    method @NonNull public android.media.MediaRoute2Info.Builder setClientPackageName(@Nullable String);
+    method @NonNull public android.media.MediaRoute2Info.Builder setConnectionState(int);
+    method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence);
+    method @NonNull public android.media.MediaRoute2Info.Builder setDeviceType(int);
+    method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle);
+    method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri);
+    method @NonNull public android.media.MediaRoute2Info.Builder setVolume(int);
+    method @NonNull public android.media.MediaRoute2Info.Builder setVolumeHandling(int);
+    method @NonNull public android.media.MediaRoute2Info.Builder setVolumeMax(int);
+  }
+
+  public abstract class MediaRoute2ProviderService extends android.app.Service {
+    ctor public MediaRoute2ProviderService();
+    method public final void notifyRoutes(@NonNull java.util.Collection<android.media.MediaRoute2Info>);
+    method @NonNull public android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public void onDiscoveryPreferenceChanged(@NonNull android.media.RouteDiscoveryPreference);
+    field public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";
+  }
+
   public class MediaRouter {
     method public void addCallback(int, android.media.MediaRouter.Callback);
     method public void addCallback(int, android.media.MediaRouter.Callback, int);
@@ -26419,6 +26492,20 @@
     method public abstract void onVolumeUpdateRequest(android.media.MediaRouter.RouteInfo, int);
   }
 
+  public class MediaRouter2 {
+    method @NonNull public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context);
+    method @NonNull public java.util.List<android.media.MediaRoute2Info> getRoutes();
+    method public void registerRouteCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteCallback, @NonNull android.media.RouteDiscoveryPreference);
+    method public void unregisterRouteCallback(@NonNull android.media.MediaRouter2.RouteCallback);
+  }
+
+  public static class MediaRouter2.RouteCallback {
+    ctor public MediaRouter2.RouteCallback();
+    method public void onRoutesAdded(@NonNull java.util.List<android.media.MediaRoute2Info>);
+    method public void onRoutesChanged(@NonNull java.util.List<android.media.MediaRoute2Info>);
+    method public void onRoutesRemoved(@NonNull java.util.List<android.media.MediaRoute2Info>);
+  }
+
   public class MediaScannerConnection implements android.content.ServiceConnection {
     ctor public MediaScannerConnection(android.content.Context, android.media.MediaScannerConnection.MediaScannerConnectionClient);
     method public void connect();
@@ -26777,6 +26864,22 @@
     field public static final int URI_COLUMN_INDEX = 2; // 0x2
   }
 
+  public final class RouteDiscoveryPreference implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.List<java.lang.String> getPreferredFeatures();
+    method public boolean isActiveScan();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteDiscoveryPreference> CREATOR;
+  }
+
+  public static final class RouteDiscoveryPreference.Builder {
+    ctor public RouteDiscoveryPreference.Builder(@NonNull java.util.List<java.lang.String>, boolean);
+    ctor public RouteDiscoveryPreference.Builder(@NonNull android.media.RouteDiscoveryPreference);
+    method @NonNull public android.media.RouteDiscoveryPreference build();
+    method @NonNull public android.media.RouteDiscoveryPreference.Builder setActiveScan(boolean);
+    method @NonNull public android.media.RouteDiscoveryPreference.Builder setPreferredFeatures(@NonNull java.util.List<java.lang.String>);
+  }
+
   public final class Session2Command implements android.os.Parcelable {
     ctor public Session2Command(int);
     ctor public Session2Command(@NonNull String, @Nullable android.os.Bundle);
@@ -28598,7 +28701,9 @@
     ctor public TvInputService();
     method public final android.os.IBinder onBind(android.content.Intent);
     method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(String);
+    method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String, @NonNull String);
     method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(String);
+    method @Nullable public android.media.tv.TvInputService.Session onCreateSession(@NonNull String, @NonNull String);
     field public static final String SERVICE_INTERFACE = "android.media.tv.TvInputService";
     field public static final String SERVICE_META_DATA = "android.media.tv.input";
   }
@@ -28701,7 +28806,7 @@
     method public boolean isEncrypted();
     method public boolean isHardOfHearing();
     method public boolean isSpokenSubtitle();
-    method public void writeToParcel(android.os.Parcel, int);
+    method public void writeToParcel(@NonNull 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
     field public static final int TYPE_SUBTITLE = 2; // 0x2
@@ -28710,21 +28815,21 @@
 
   public static final class TvTrackInfo.Builder {
     ctor public TvTrackInfo.Builder(int, @NonNull String);
-    method public android.media.tv.TvTrackInfo build();
-    method public android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int);
+    method @NonNull public android.media.tv.TvTrackInfo build();
+    method @NonNull public android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int);
     method @NonNull public android.media.tv.TvTrackInfo.Builder setAudioDescription(boolean);
-    method public android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int);
-    method public android.media.tv.TvTrackInfo.Builder setDescription(CharSequence);
+    method @NonNull public android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int);
+    method @NonNull public android.media.tv.TvTrackInfo.Builder setDescription(@NonNull 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 setExtra(@NonNull 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 setLanguage(@NonNull 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);
-    method public android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
-    method public android.media.tv.TvTrackInfo.Builder setVideoWidth(int);
+    method @NonNull public android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
+    method @NonNull public android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
+    method @NonNull public android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
+    method @NonNull public android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
+    method @NonNull public android.media.tv.TvTrackInfo.Builder setVideoWidth(int);
   }
 
   public class TvView extends android.view.ViewGroup {
@@ -30541,6 +30646,7 @@
     field public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
     field public static final String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
     field public static final String ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION = "android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION";
+    field public static final String ACTION_WIFI_SCAN_AVAILABLE = "android.net.wifi.action.WIFI_SCAN_AVAILABLE";
     field @Deprecated public static final int ERROR_AUTHENTICATING = 1; // 0x1
     field @Deprecated public static final String EXTRA_BSSID = "bssid";
     field public static final String EXTRA_NETWORK_INFO = "networkInfo";
@@ -30549,6 +30655,7 @@
     field @Deprecated public static final String EXTRA_NEW_STATE = "newState";
     field public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
     field public static final String EXTRA_RESULTS_UPDATED = "resultsUpdated";
+    field public static final String EXTRA_SCAN_AVAILABLE = "android.net.wifi.extra.SCAN_AVAILABLE";
     field @Deprecated public static final String EXTRA_SUPPLICANT_CONNECTED = "connected";
     field @Deprecated public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
     field @Deprecated public static final String EXTRA_WIFI_INFO = "wifiInfo";
@@ -39343,6 +39450,7 @@
     field @Deprecated public static final String LONGITUDE = "longitude";
     field @Deprecated public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
     field @Deprecated public static final String PICASA_ID = "picasa_id";
+    field public static final String SCENE_CAPTURE_TYPE = "scene_capture_type";
   }
 
   public static final class MediaStore.Images.Media implements android.provider.MediaStore.Images.ImageColumns {
@@ -42685,14 +42793,19 @@
     method public android.content.Intent createReEnrollIntent();
     method public android.content.Intent createUnEnrollIntent();
     method public int getParameter(int);
+    method public int getSupportedAudioCapabilities();
     method public int getSupportedRecognitionModes();
     method @Nullable public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int);
     method public int setParameter(int, int);
     method public boolean startRecognition(int);
     method public boolean stopRecognition();
+    field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
+    field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
     field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
     field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2
     field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1
+    field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION = 4; // 0x4
+    field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 8; // 0x8
     field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
     field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1
     field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
@@ -45051,7 +45164,7 @@
     field public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
     field public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool";
     field public static final String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
-    field public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
+    field @Deprecated public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
     field public static final String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
     field public static final String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
     field public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL = "call_barring_supports_deactivate_all_bool";
@@ -45074,6 +45187,7 @@
     field public static final String KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT = "carrier_instant_lettering_length_limit_int";
     field public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
     field public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
+    field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool";
     field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
     field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
     field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
@@ -45542,6 +45656,11 @@
     method @Nullable public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, @NonNull java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
   }
 
+  public 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);
@@ -45779,8 +45898,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);
@@ -45789,7 +45908,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);
@@ -54703,6 +54822,7 @@
 
   public final class InlineSuggestionsRequest implements android.os.Parcelable {
     method public int describeContents();
+    method @NonNull public String getHostPackageName();
     method public int getMaxSuggestionCount();
     method @NonNull public java.util.List<android.view.inline.InlinePresentationSpec> getPresentationSpecs();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/api/module-app-current.txt b/api/module-app-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/api/module-app-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/api/module-app-removed.txt b/api/module-app-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/api/module-app-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/api/module-lib-removed.txt b/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/api/system-current.txt b/api/system-current.txt
index e0f6422..789d86f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -190,6 +190,7 @@
     field public static final String REVIEW_ACCESSIBILITY_SERVICES = "android.permission.REVIEW_ACCESSIBILITY_SERVICES";
     field public static final String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
     field public static final String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
+    field public static final String SECURE_ELEMENT_PRIVILEGED = "android.permission.SECURE_ELEMENT_PRIVILEGED";
     field public static final String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY";
     field public static final String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
     field public static final String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
@@ -210,6 +211,7 @@
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
     field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
     field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
+    field public static final String TUNER_RESOURCE_ACCESS = "android.permission.TUNER_RESOURCE_ACCESS";
     field public static final String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
     field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
     field public static final String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS";
@@ -282,6 +284,7 @@
     field public static final int config_helpIntentNameKey = 17039390; // 0x104001e
     field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
     field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
+    field public static final int config_systemGallery = 17039402; // 0x104002a
   }
 
   public static final class R.style {
@@ -812,7 +815,9 @@
     field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
     field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
     field public static final String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION";
+    field public static final String ACTION_PROVISION_FINANCED_DEVICE = "android.app.action.PROVISION_FINANCED_DEVICE";
     field public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
+    field public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED = "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
     field public static final String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
     field public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
     field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
@@ -1329,6 +1334,10 @@
     field public static final String SERVICE_INTERFACE = "android.app.usage.CacheQuotaService";
   }
 
+  public class NetworkStatsManager {
+    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.net.netstats.provider.NetworkStatsProviderCallback registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.AbstractNetworkStatsProvider);
+  }
+
   public static final class UsageEvents.Event {
     method public int getInstanceId();
     method @Nullable public String getNotificationChannelId();
@@ -1705,6 +1714,7 @@
     field public static final String NETD_SERVICE = "netd";
     field public static final String NETWORK_POLICY_SERVICE = "netpolicy";
     field public static final String NETWORK_SCORE_SERVICE = "network_score";
+    field public static final String NETWORK_STACK_SERVICE = "network_stack";
     field public static final String OEM_LOCK_SERVICE = "oem_lock";
     field public static final String PERMISSION_SERVICE = "permission";
     field public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
@@ -2407,7 +2417,7 @@
 package android.hardware.camera2 {
 
   public abstract class CameraDevice implements java.lang.AutoCloseable {
-    method public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method @Deprecated public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     field public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // 0x1
     field public static final int SESSION_OPERATION_MODE_NORMAL = 0; // 0x0
     field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000
@@ -3566,7 +3576,10 @@
   public static final class SoundTrigger.ModuleProperties implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
+    field public static final int CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> CREATOR;
+    field public final int audioCapabilities;
     field @NonNull public final String description;
     field public final int id;
     field @NonNull public final String implementor;
@@ -4063,6 +4076,10 @@
     field public static final int ROLE_OUTPUT = 2; // 0x2
   }
 
+  public final class AudioDeviceInfo {
+    field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
+  }
+
   public final class AudioFocusInfo implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.media.AudioAttributes getAttributes();
@@ -4090,6 +4107,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups();
+    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAddress> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAddress getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
@@ -4357,6 +4375,8 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public boolean stopRecognition();
     field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2
     field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1
+    field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION = 4; // 0x4
+    field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 8; // 0x8
   }
 
   public abstract static class SoundTriggerDetector.Callback {
@@ -4386,10 +4406,12 @@
   }
 
   public static class SoundTriggerManager.Model {
-    method public static android.media.soundtrigger.SoundTriggerManager.Model create(java.util.UUID, java.util.UUID, byte[]);
-    method public byte[] getModelData();
-    method public java.util.UUID getModelUuid();
-    method public java.util.UUID getVendorUuid();
+    method @NonNull public static android.media.soundtrigger.SoundTriggerManager.Model create(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], int);
+    method @NonNull public static android.media.soundtrigger.SoundTriggerManager.Model create(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[]);
+    method @Nullable public byte[] getModelData();
+    method @NonNull public java.util.UUID getModelUuid();
+    method @NonNull public java.util.UUID getVendorUuid();
+    method public int getVersion();
   }
 
 }
@@ -4519,6 +4541,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating);
     method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig);
     method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String);
+    method @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS) public int getClientPid(@NonNull String);
     method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
     method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList();
     method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList();
@@ -4530,6 +4553,7 @@
     method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public void releaseTvInputHardware(int, android.media.tv.TvInputManager.Hardware);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void removeBlockedRating(@NonNull android.media.tv.TvContentRating);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void setParentalControlsEnabled(boolean);
+    field public static final int UNKNOWN_CLIENT_PID = -1; // 0xffffffff
   }
 
   public static final class TvInputManager.Hardware {
@@ -4606,6 +4630,28 @@
     method public abstract int getType();
   }
 
+  public class Lnb implements java.lang.AutoCloseable {
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void close();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int sendDiseqcMessage(@NonNull byte[]);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setSatellitePosition(int);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setTone(int);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setVoltage(int);
+    field public static final int POSITION_A = 1; // 0x1
+    field public static final int POSITION_B = 2; // 0x2
+    field public static final int POSITION_UNDEFINED = 0; // 0x0
+    field public static final int TONE_CONTINUOUS = 1; // 0x1
+    field public static final int TONE_NONE = 0; // 0x0
+    field public static final int VOLTAGE_11V = 2; // 0x2
+    field public static final int VOLTAGE_12V = 3; // 0x3
+    field public static final int VOLTAGE_13V = 4; // 0x4
+    field public static final int VOLTAGE_14V = 5; // 0x5
+    field public static final int VOLTAGE_15V = 6; // 0x6
+    field public static final int VOLTAGE_18V = 7; // 0x7
+    field public static final int VOLTAGE_19V = 8; // 0x8
+    field public static final int VOLTAGE_5V = 1; // 0x1
+    field public static final int VOLTAGE_NONE = 0; // 0x0
+  }
+
   public final class Tuner implements java.lang.AutoCloseable {
     ctor public Tuner(@NonNull android.content.Context);
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Tuner.Descrambler openDescrambler();
@@ -4628,16 +4674,43 @@
     field public static final int FILTER_STATUS_HIGH_WATER = 4; // 0x4
     field public static final int FILTER_STATUS_LOW_WATER = 2; // 0x2
     field public static final int FILTER_STATUS_OVERFLOW = 8; // 0x8
+    field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
+    field public static final int RESULT_INVALID_STATE = 3; // 0x3
+    field public static final int RESULT_NOT_INITIALIZED = 2; // 0x2
+    field public static final int RESULT_OUT_OF_MEMORY = 5; // 0x5
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+    field public static final int RESULT_UNAVAILABLE = 1; // 0x1
+    field public static final int RESULT_UNKNOWN_ERROR = 6; // 0x6
   }
 
 }
 
 package android.media.tv.tuner.filter {
 
+  public abstract class FilterConfiguration {
+    field public static final int FILTER_TYPE_ALP = 16; // 0x10
+    field public static final int FILTER_TYPE_IP = 4; // 0x4
+    field public static final int FILTER_TYPE_MMTP = 2; // 0x2
+    field public static final int FILTER_TYPE_TLV = 8; // 0x8
+    field public static final int FILTER_TYPE_TS = 1; // 0x1
+  }
+
   public abstract class FilterEvent {
     ctor public FilterEvent();
   }
 
+  public class PesSettings extends android.media.tv.tuner.filter.Settings {
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.PesSettings.Builder builder(@NonNull android.content.Context, int);
+    method public int getStreamId();
+    method public boolean isRaw();
+  }
+
+  public static class PesSettings.Builder {
+    method @NonNull public android.media.tv.tuner.filter.PesSettings build();
+    method @NonNull public android.media.tv.tuner.filter.PesSettings.Builder setRaw(boolean);
+    method @NonNull public android.media.tv.tuner.filter.PesSettings.Builder setStreamId(int);
+  }
+
   public class SectionEvent extends android.media.tv.tuner.filter.FilterEvent {
     method public int getDataLength();
     method public int getSectionNumber();
@@ -4645,6 +4718,22 @@
     method public int getVersion();
   }
 
+  public abstract class Settings {
+  }
+
+  public class TsFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.TsFilterConfiguration.Builder builder(@NonNull android.content.Context);
+    method @Nullable public android.media.tv.tuner.filter.Settings getSettings();
+    method public int getTpid();
+    method public int getType();
+  }
+
+  public static class TsFilterConfiguration.Builder {
+    method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration build();
+    method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration.Builder setSettings(@NonNull android.media.tv.tuner.filter.Settings);
+    method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration.Builder setTpid(int);
+  }
+
 }
 
 package android.metrics {
@@ -4695,7 +4784,9 @@
 
   public class CaptivePortal implements android.os.Parcelable {
     method public void logEvent(int, @NonNull String);
+    method public void reevaluateNetwork();
     method public void useNetwork();
+    field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64
     field public static final int APP_RETURN_DISMISSED = 0; // 0x0
     field public static final int APP_RETURN_UNWANTED = 1; // 0x1
     field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
@@ -5842,7 +5933,9 @@
 
   public final class SoftApConfiguration implements android.os.Parcelable {
     method public int describeContents();
+    method @NonNull public java.util.List<android.net.MacAddress> getAllowedClientList();
     method public int getBand();
+    method @NonNull public java.util.List<android.net.MacAddress> getBlockedClientList();
     method @Nullable public android.net.MacAddress getBssid();
     method public int getChannel();
     method public int getMaxNumberOfClients();
@@ -5850,6 +5943,7 @@
     method public int getSecurityType();
     method public int getShutdownTimeoutMillis();
     method @Nullable public String getSsid();
+    method public boolean isClientControlByUserEnabled();
     method public boolean isHiddenSsid();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field public static final int BAND_2GHZ = 1; // 0x1
@@ -5867,9 +5961,11 @@
     ctor public SoftApConfiguration.Builder();
     ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration);
     method @NonNull public android.net.wifi.SoftApConfiguration build();
+    method @NonNull public android.net.wifi.SoftApConfiguration.Builder enableClientControlByUser(boolean);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBand(int);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int);
+    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientList(@NonNull java.util.List<android.net.MacAddress>, @NonNull java.util.List<android.net.MacAddress>);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(int);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int);
@@ -6108,6 +6204,8 @@
     field public static final int IFACE_IP_MODE_UNSPECIFIED = -1; // 0xffffffff
     field public static final int PASSPOINT_HOME_NETWORK = 0; // 0x0
     field public static final int PASSPOINT_ROAMING_NETWORK = 1; // 0x1
+    field public static final int SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER = 0; // 0x0
+    field public static final int SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS = 1; // 0x1
     field public static final int SAP_START_FAILURE_GENERAL = 0; // 0x0
     field public static final int SAP_START_FAILURE_NO_CHANNEL = 1; // 0x1
     field public static final int SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION = 2; // 0x2
@@ -6149,6 +6247,7 @@
   }
 
   public static interface WifiManager.SoftApCallback {
+    method public default void onBlockedClientConnecting(@NonNull android.net.wifi.WifiClient, int);
     method public default void onCapabilityChanged(@NonNull android.net.wifi.SoftApCapability);
     method public default void onConnectedClientsChanged(@NonNull java.util.List<android.net.wifi.WifiClient>);
     method public default void onInfoChanged(@NonNull android.net.wifi.SoftApInfo);
@@ -6182,6 +6281,25 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int);
   }
 
+  public final class WifiOemConfigStoreMigrationHook {
+    method @Nullable public static android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData load();
+  }
+
+  public static final class WifiOemConfigStoreMigrationHook.MigrationData implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public java.util.List<android.net.wifi.WifiConfiguration> getUserSavedNetworkConfigurations();
+    method @Nullable public android.net.wifi.SoftApConfiguration getUserSoftApConfiguration();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData> CREATOR;
+  }
+
+  public static final class WifiOemConfigStoreMigrationHook.MigrationData.Builder {
+    ctor public WifiOemConfigStoreMigrationHook.MigrationData.Builder();
+    method @NonNull public android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData build();
+    method @NonNull public android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>);
+    method @NonNull public android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
+  }
+
   public class WifiScanner {
     method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]);
     method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
@@ -8037,7 +8155,6 @@
     field @NonNull public static final String ENABLE_CMAS_PRESIDENTIAL_PREF = "enable_cmas_presidential_alerts";
     field @NonNull public static final String ENABLE_CMAS_SEVERE_THREAT_PREF = "enable_cmas_severe_threat_alerts";
     field @NonNull public static final String ENABLE_EMERGENCY_PERF = "enable_emergency_alerts";
-    field @NonNull public static final String ENABLE_FULL_VOLUME_PREF = "use_full_volume";
     field @NonNull public static final String ENABLE_PUBLIC_SAFETY_PREF = "enable_public_safety_messages";
     field @NonNull public static final String ENABLE_STATE_LOCAL_TEST_PREF = "enable_state_local_test_alerts";
     field @NonNull public static final String ENABLE_TEST_ALERT_PREF = "enable_test_alerts";
@@ -9273,6 +9390,7 @@
 
   public final class CallQuality implements android.os.Parcelable {
     ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
+    ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
     method public int describeContents();
     method public int getAverageRelativeJitter();
     method public int getAverageRoundTripTime();
@@ -9285,6 +9403,9 @@
     method public int getNumRtpPacketsTransmitted();
     method public int getNumRtpPacketsTransmittedLost();
     method public int getUplinkCallQualityLevel();
+    method public boolean isIncomingSilenceDetected();
+    method public boolean isOutgoingSilenceDetected();
+    method public boolean isRtpInactivityDetected();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CALL_QUALITY_BAD = 4; // 0x4
     field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0
@@ -10284,9 +10405,19 @@
     method public boolean disableCellBroadcastRange(int, int, int);
     method public boolean enableCellBroadcastRange(int, int, int);
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public java.util.List<android.telephony.SmsMessage> getMessagesFromIcc();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getPremiumSmsConsent(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSmsCapacityOnIcc();
     method public void sendMultipartTextMessage(@NonNull String, @NonNull String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPremiumSmsConsent(@NonNull String, int);
+    field public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3; // 0x3
+    field public static final int PREMIUM_SMS_CONSENT_ASK_USER = 1; // 0x1
+    field public static final int PREMIUM_SMS_CONSENT_NEVER_ALLOW = 2; // 0x2
+    field public static final int PREMIUM_SMS_CONSENT_UNKNOWN = 0; // 0x0
+  }
+
+  public class SmsMessage {
+    method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static byte[] getSubmitPduEncodedMessage(boolean, @NonNull String, @NonNull String, int, int, int, int, int, int);
   }
 
   public class SubscriptionInfo implements android.os.Parcelable {
@@ -10396,6 +10527,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
     method public int getEmergencyNumberDbVersion();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
+    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getIsimImpu();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
     method public int getMaxNumberOfSimultaneouslyActiveSims();
@@ -10480,12 +10612,16 @@
     method public void updateServiceLocation();
     method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String);
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_ANOMALY_REPORTED = "android.telephony.action.ANOMALY_REPORTED";
+    field public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
+    field public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE";
+    field public static final String ACTION_CARRIER_SIGNAL_REDIRECTED = "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED";
+    field public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED = "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED";
+    field public static final String ACTION_CARRIER_SIGNAL_RESET = "com.android.internal.telephony.CARRIER_SIGNAL_RESET";
     field public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED";
     field public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED";
     field public static final String ACTION_EMERGENCY_ASSISTANCE = "android.telephony.action.EMERGENCY_ASSISTANCE";
     field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
     field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
-    field public static final String ACTION_NETWORK_SET_TIME = "android.telephony.action.NETWORK_SET_TIME";
     field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE";
     field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
     field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
@@ -10497,6 +10633,15 @@
     field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
     field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
     field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
+    field @Deprecated public static final String EXTRA_APN_PROTOCOL = "apnProto";
+    field public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt";
+    field @Deprecated public static final String EXTRA_APN_TYPE = "apnType";
+    field public static final String EXTRA_APN_TYPE_INT = "apnTypeInt";
+    field public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable";
+    field public static final String EXTRA_ERROR_CODE = "errorCode";
+    field public static final String EXTRA_PCO_ID = "pcoId";
+    field public static final String EXTRA_PCO_VALUE = "pcoValue";
+    field public static final String EXTRA_REDIRECTION_URL = "redirectionUrl";
     field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
     field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
     field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
@@ -11101,6 +11246,7 @@
   public class ImsManager {
     method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
     method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
+    field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
   }
 
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
@@ -11395,13 +11541,29 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getProvisioningIntValue(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int);
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
+    field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
+    field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
+    field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
+    field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
+    field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11
+    field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17
+    field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16
+    field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
+    field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10
+    field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
+    field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
     field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
     field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
+    field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
+    field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
+    field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
     field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
     field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
     field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
@@ -11414,6 +11576,47 @@
     method public void onProvisioningStringChanged(int, @NonNull String);
   }
 
+  public final class RcsContactUceCapability implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.List<java.lang.String> getCapableExtensionTags();
+    method @NonNull public android.net.Uri getContactUri();
+    method @Nullable public android.net.Uri getServiceUri(int);
+    method public boolean isCapable(int);
+    method public boolean isCapable(@NonNull String);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CAPABILITY_CHAT_SESSION = 2; // 0x2
+    field public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = 4; // 0x4
+    field public static final int CAPABILITY_CHAT_STANDALONE = 1; // 0x1
+    field public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = 4096; // 0x1000
+    field public static final int CAPABILITY_FILE_TRANSFER = 8; // 0x8
+    field public static final int CAPABILITY_FILE_TRANSFER_HTTP = 64; // 0x40
+    field public static final int CAPABILITY_FILE_TRANSFER_SMS = 128; // 0x80
+    field public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = 32; // 0x20
+    field public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = 16; // 0x10
+    field public static final int CAPABILITY_GEOLOCATION_PULL = 131072; // 0x20000
+    field public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = 262144; // 0x40000
+    field public static final int CAPABILITY_GEOLOCATION_PUSH = 32768; // 0x8000
+    field public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = 65536; // 0x10000
+    field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100
+    field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000
+    field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000
+    field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000
+    field public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = 2097152; // 0x200000
+    field public static final int CAPABILITY_RCS_VOICE_CALL = 524288; // 0x80000
+    field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800
+    field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400
+    field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
+  }
+
+  public static class RcsContactUceCapability.Builder {
+    ctor public RcsContactUceCapability.Builder(@NonNull android.net.Uri);
+    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(int, @NonNull android.net.Uri);
+    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(int);
+    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(@NonNull String);
+    method @NonNull public android.telephony.ims.RcsContactUceCapability build();
+  }
+
   public interface RegistrationManager {
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
diff --git a/api/test-current.txt b/api/test-current.txt
index 00a2c29..1db4c9b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -34,6 +34,7 @@
   public static final class R.string {
     field public static final int config_defaultAssistant = 17039393; // 0x1040021
     field public static final int config_defaultDialer = 17039395; // 0x1040023
+    field public static final int config_systemGallery = 17039402; // 0x104002a
   }
 
 }
@@ -756,6 +757,7 @@
     field public static final String BUGREPORT_SERVICE = "bugreport";
     field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
     field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
+    field public static final String NETWORK_STACK_SERVICE = "network_stack";
     field public static final String PERMISSION_SERVICE = "permission";
     field public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
     field public static final String ROLLBACK_SERVICE = "rollback";
@@ -1020,7 +1022,7 @@
 package android.hardware.camera2 {
 
   public abstract class CameraDevice implements java.lang.AutoCloseable {
-    method public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method @Deprecated public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     field public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // 0x1
     field public static final int SESSION_OPERATION_MODE_NORMAL = 0; // 0x0
     field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000
@@ -1496,7 +1498,9 @@
 
   public class CaptivePortal implements android.os.Parcelable {
     method public void logEvent(int, @NonNull String);
+    method public void reevaluateNetwork();
     method public void useNetwork();
+    field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64
     field public static final int APP_RETURN_DISMISSED = 0; // 0x0
     field public static final int APP_RETURN_UNWANTED = 1; // 0x1
     field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
@@ -2733,6 +2737,11 @@
     method @Nullable public android.util.SparseArray<android.service.autofill.InternalOnClickAction> getActions();
   }
 
+  public static final class Dataset.Builder {
+    ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
+    method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
+  }
+
   public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception;
   }
@@ -3127,6 +3136,7 @@
 
   public final class CallQuality implements android.os.Parcelable {
     ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
+    ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
     method public int describeContents();
     method public int getAverageRelativeJitter();
     method public int getAverageRoundTripTime();
@@ -3139,6 +3149,9 @@
     method public int getNumRtpPacketsTransmitted();
     method public int getNumRtpPacketsTransmittedLost();
     method public int getUplinkCallQualityLevel();
+    method public boolean isIncomingSilenceDetected();
+    method public boolean isOutgoingSilenceDetected();
+    method public boolean isRtpInactivityDetected();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CALL_QUALITY_BAD = 4; // 0x4
     field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0
@@ -3522,6 +3535,7 @@
   public class ImsManager {
     method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
     method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
+    field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
   }
 
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
@@ -3812,13 +3826,29 @@
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public int getProvisioningIntValue(int);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
     method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public String getProvisioningStringValue(int);
+    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
+    field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
+    field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
+    field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
+    field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
+    field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11
+    field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17
+    field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16
+    field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
+    field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10
+    field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
+    field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
     field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
     field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
+    field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
+    field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
+    field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
     field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
     field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
     field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
@@ -4249,6 +4279,7 @@
     field public static final String FFLAG_OVERRIDE_PREFIX = "sys.fflag.override.";
     field public static final String FFLAG_PREFIX = "sys.fflag.";
     field public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
+    field public static final String NOTIF_CONVO_BYPASS_SHORTCUT_REQ = "settings_notif_convo_bypass_shortcut_req";
     field public static final String PERSIST_PREFIX = "persist.sys.fflag.override.";
     field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
     field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 459520a..8fac31a 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -1113,7 +1113,7 @@
         SurfaceComposerClient::Transaction t;
         t.setPosition(mFlingerSurfaceControl, 0, -mTargetInset)
                 .setCrop(mFlingerSurfaceControl, Rect(0, mTargetInset, mWidth, mHeight));
-        t.setDisplayProjection(mDisplayToken, 0 /* orientation */, layerStackRect, displayRect);
+        t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, layerStackRect, displayRect);
         t.apply();
 
         mTargetInset = mCurrentInset = 0;
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index 229628c..4074789 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -33,6 +33,8 @@
 
 namespace {
 
+#define REWRITE_PACKAGE(resid, package_id) \
+  (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U))
 #define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
 
 std::string ConcatPolicies(const std::vector<std::string>& policies) {
@@ -154,6 +156,7 @@
     return Error("root element is not <overlay> tag");
   }
 
+  const uint8_t target_package_id = target_package->GetPackageId();
   const uint8_t overlay_package_id = overlay_package->GetPackageId();
   auto overlay_it_end = root_it.end();
   for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
@@ -187,6 +190,9 @@
       continue;
     }
 
+    // Retrieve the compile-time resource id of the target resource.
+    target_id = REWRITE_PACKAGE(target_id, target_package_id);
+
     if (overlay_resource->dataType == Res_value::TYPE_STRING) {
       overlay_resource->data += string_pool_offset;
     }
@@ -214,6 +220,7 @@
     const AssetManager2* target_am, const AssetManager2* overlay_am,
     const LoadedPackage* target_package, const LoadedPackage* overlay_package) {
   ResourceMapping resource_mapping;
+  const uint8_t target_package_id = target_package->GetPackageId();
   const auto end = overlay_package->end();
   for (auto iter = overlay_package->begin(); iter != end; ++iter) {
     const ResourceId overlay_resid = *iter;
@@ -225,11 +232,14 @@
     // Find the resource with the same type and entry name within the target package.
     const std::string full_name =
         base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
-    const ResourceId target_resource = target_am->GetResourceId(full_name);
+    ResourceId target_resource = target_am->GetResourceId(full_name);
     if (target_resource == 0U) {
       continue;
     }
 
+    // Retrieve the compile-time resource id of the target resource.
+    target_resource = REWRITE_PACKAGE(target_resource, target_package_id);
+
     resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid,
                                 /* rewrite_overlay_reference */ false);
   }
diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS
index 04464ce..a61babf 100644
--- a/cmds/statsd/OWNERS
+++ b/cmds/statsd/OWNERS
@@ -1,7 +1,8 @@
-jianjin@google.com
+jeffreyhuang@google.com
 joeo@google.com
 jtnguyen@google.com
 muhammadq@google.com
+ruchirr@google.com
 singhtejinder@google.com
 tsaichristine@google.com
 yaochen@google.com
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 6fc1e23..967fd32 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -261,6 +261,11 @@
     return Matcher(Field(tag, getSimpleField(field)), 0xff7f0000);
 }
 
+inline Matcher getFirstUidMatcher(int32_t atomId) {
+    int32_t pos[] = {1, 1, 1};
+    return Matcher(Field(atomId, pos, 2), 0xff7f7f7f);
+}
+
 /**
  * A wrapper for a union type to contain multiple types of values.
  *
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 6d2bd04..946c550 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -30,6 +30,8 @@
     PRIMARY = 1;
     // The field that represents the state. It's an exclusive state.
     EXCLUSIVE = 2;
+
+    PRIMARY_FIELD_FIRST_UID = 3;
 }
 
 // Used to annotate an atom that reprsents a state change. A state change atom must have exactly ONE
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 2bacfbc..bb01911 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -126,10 +126,10 @@
         AppStartOccurred app_start_occurred = 48;
         AppStartCanceled app_start_canceled = 49;
         AppStartFullyDrawn app_start_fully_drawn = 50;
-        LmkKillOccurred lmk_kill_occurred = 51;
+        LmkKillOccurred lmk_kill_occurred = 51 [(module) = "lmkd"];
         PictureInPictureStateChanged picture_in_picture_state_changed = 52;
         WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53 [(module) = "wifi"];
-        LmkStateChanged lmk_state_changed = 54;
+        LmkStateChanged lmk_state_changed = 54 [(module) = "lmkd"];
         AppStartMemoryStateCaptured app_start_memory_state_captured = 55;
         ShutdownSequenceReported shutdown_sequence_reported = 56;
         BootSequenceReported boot_sequence_reported = 57;
@@ -334,6 +334,10 @@
         MediaProviderIdleMaintenance media_provider_idle_maintenance =
             237 [(module) = "mediaprovider"];
         RebootEscrowRecoveryReported reboot_escrow_recovery_reported = 238;
+        BootTimeEventDuration boot_time_event_duration_reported = 239;
+        BootTimeEventElapsedTime boot_time_event_elapsed_time_reported = 240;
+        BootTimeEventUtcTime boot_time_event_utc_time_reported = 241;
+        BootTimeEventErrorCode boot_time_event_error_code_reported = 242;
     }
 
     // Pulled events will start at field 10000.
@@ -908,14 +912,16 @@
  *   TODO
  */
 message WakelockStateChanged {
-    repeated AttributionNode attribution_node = 1;
+    repeated AttributionNode attribution_node = 1
+            [(state_field_option).option = PRIMARY_FIELD_FIRST_UID];
 
     // The type (level) of the wakelock; e.g. a partial wakelock or a full wakelock.
     // From frameworks/base/core/proto/android/os/enums.proto.
-    optional android.os.WakeLockLevelEnum type = 2;
+    optional android.os.WakeLockLevelEnum type = 2 [(state_field_option).option = PRIMARY];
+    ;
 
     // The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
-    optional string tag = 3;
+    optional string tag = 3 [(state_field_option).option = PRIMARY];
 
     enum State {
         RELEASE = 0;
@@ -923,7 +929,7 @@
         CHANGE_RELEASE = 2;
         CHANGE_ACQUIRE = 3;
     }
-    optional State state = 4;
+    optional State state = 4 [(state_field_option).option = EXCLUSIVE];
 }
 
 /**
@@ -3925,6 +3931,207 @@
     optional float normalized_expired_media = 5;
 }
 
+/**
+ * Represents boot time event with duration in ms.
+ *
+ * Logged from: bootstat and various system server components. Check each enums for details.
+ */
+message BootTimeEventDuration {
+    enum DurationEvent {
+        UNKNOWN = 0;
+        // Bootloader time excluding BOOTLOADER_UI_WAIT + boot complete time. Logged from bootstat.
+        ABSOLUTE_BOOT_TIME = 1;
+        // Bootloader's 1st stage execution time.
+        // Logged from bootstat.
+        BOOTLOADER_FIRST_STAGE_EXEC = 2;
+        // Bootloader's 1st stage loading time.
+        // Logged from bootstat.
+        BOOTLOADER_FIRST_STAGE_LOAD = 3;
+        // Bootloader's kernel loading time.
+        // Logged from bootstat.
+        BOOTLOADER_KERNEL_LOAD = 4;
+        // Bootloader's 2nd stage execution time.
+        // Logged from bootstat.
+        BOOTLOADER_SECOND_STAGE_EXEC = 5;
+        // Bootloader's 2nd stage loading time.
+        // Logged from bootstat.
+        BOOTLOADER_SECOND_STAGE_LOAD = 6;
+        // Duration for Bootloader to show unlocked device's warning UI. This should not happen
+        // for locked device.
+        // Logged from bootstat.
+        BOOTLOADER_UI_WAIT = 7;
+        // Total time spend in bootloader. This is the sum of all BOOTLOADER_* listed above.
+        // Logged from bootstat.
+        BOOTLOADER_TOTAL = 8;
+        // Shutdown duration inside init for the reboot before the current boot up.
+        // Logged from f/b/services/.../BootReceiver.java.
+        SHUTDOWN_DURATION = 9;
+        // Total time for mounting of disk devices during bootup.
+        // Logged from f/b/services/.../BootReceiver.java.
+        MOUNT_DEFAULT_DURATION = 10;
+        // Total time for early stage mounting of disk devices during bootup.
+        // Logged from f/b/services/.../BootReceiver.java.
+        MOUNT_EARLY_DURATION = 11;
+        // Total time for late stage mounting of disk devices during bootup.
+        // Logged from f/b/services/.../BootReceiver.java.
+        MOUNT_LATE_DURATION = 12;
+        // Average time to scan non-system app after OTA
+        // Logged from f/b/services/.../PackageManagerService.java
+        OTA_PACKAGE_MANAGER_INIT_TIME = 13;
+        // Time to initialize Package manager after OTA
+        // Logged from f/b/services/.../PackageManagerService.java
+        OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME = 14;
+        // Time to scan all system app from Package manager after OTA
+        // Logged from f/b/services/.../PackageManagerService.java
+        OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME = 15;
+        // Init's total time for cold boot stage.
+        // Logged from bootstat.
+        COLDBOOT_WAIT = 16;
+        // Init's total time for initializing selinux.
+        // Logged from bootstat.
+        SELINUX_INIT = 17;
+        // Time since last factory reset.
+        // Logged from bootstat.
+        FACTORY_RESET_TIME_SINCE_RESET = 18;
+    }
+
+    // Type of the event.
+    optional DurationEvent event = 1;
+    // Duration of the event in ms.
+    optional int64 duration_millis = 2;
+}
+
+/**
+ * Represents the start of specific boot time event during bootup in ms. This is usually a time
+ * since boot-up.
+ *
+ * Logged from: bootstat and various system server components. Check each enums for details.
+ */
+message BootTimeEventElapsedTime {
+    enum ElapsedTimeEvent {
+        UNKNOWN = 0;
+        // Time when init starts 1st stage. Logged from bootstat.
+        ANDROID_INIT_STAGE_1 = 1;
+        // Time when sys.boot_completed prop is set.
+        // Logged from bootstat.
+        BOOT_COMPLETE = 2;
+        // BOOT_COMPLETE for encrypted device.
+        BOOT_COMPLETE_ENCRYPTION = 3;
+        // BOOT_COMPLETE for device with no encryption.
+        BOOT_COMPLETE_NO_ENCRYPTION = 4;
+        // Adjusted BOOT_COMPLETE for encrypted device extracting decryption time.
+        BOOT_COMPLETE_POST_DESCRYPT = 5;
+        // BOOT_COMPLETE after factory reset.
+        FACTORY_RESET_BOOT_COMPLETE = 6;
+        // BOOT_COMPLETE_NO_ENCRYPTION after factory reset.
+        FACTORY_RESET_BOOT_COMPLETE_NO_ENCRYPTION = 7;
+        // BOOT_COMPLETE_POST_DESCRYPT after factory reset.
+        FACTORY_RESET_BOOT_COMPLETE_POST_DESCRYPT = 8;
+        // BOOT_COMPLETE after OTA.
+        OTA_BOOT_COMPLETE = 9;
+        // BOOT_COMPLETE_NO_ENCRYPTION after OTA.
+        OTA_BOOT_COMPLETE_NO_ENCRYPTION = 10;
+        // BOOT_COMPLETE_POST_DESCRYPT after OTA.
+        OTA_BOOT_COMPLETE_POST_DESCRYPT = 11;
+        // Time when the system starts sending LOCKED_BOOT_COMPLETED broadcast.
+        // Logged from  f/b/services/.../UserController.java
+        FRAMEWORK_LOCKED_BOOT_COMPLETED = 12;
+        // Time when the system starts sending BOOT_COMPLETED broadcast.
+        // Logged from  f/b/services/.../UserController.java
+        FRAMEWORK_BOOT_COMPLETED = 13;
+        // Time when the package manager starts init.
+        // Logged from f/b/services/.../SystemServer.java
+        PACKAGE_MANAGER_INIT_START = 14;
+        // Time when package manager is ready
+        // Logged from f/b/services/.../SystemServer.java
+        PACKAGE_MANAGER_INIT_READY = 15;
+        // Represents the time when user has entered unlock credential for system with user pin.
+        // Logged from bootstat.
+        POST_DECRYPT = 16;
+        // Represents the start of zygote's init.
+        // Logged from zygote itself.
+        ZYGOTE_INIT_START = 17;
+        // Represents the start of secondary zygote's init.
+        // TODO: add logging to zygote
+        SECONDARY_ZYGOTE_INIT_START = 18;
+        // Represents the start of system server's init.
+        // Logged from f/b/services/.../SystemServer.java
+        SYSTEM_SERVER_INIT_START = 19;
+        // Represents the completion of system server's init.
+        // Logged from f/b/services/.../SystemServer.java
+        SYSTEM_SERVER_READY = 20;
+        // Represents the start of launcher during boot-up.
+        // TODO: add logging
+        LAUNCHER_START = 21;
+        // Represents the completion of launcher's initial rendering. User can use other apps from
+        // launcher from this point.
+        // TODO: add logging
+        LAUNCHER_SHOWN = 22;
+    }
+
+    // Type of the event.
+    optional ElapsedTimeEvent event = 1;
+    // Time since bootup for the event.
+    // It should be acquired from SystemClock elapsedRealtime() call or equivalent.
+    optional int64 time_millis = 2;
+}
+
+/**
+ * Boot time events with UTC time.
+ *
+ * Logged from: bootstat and various system server components. Check each enums for details.
+ */
+message BootTimeEventUtcTime {
+    enum UtcTimeEvent {
+        UNKNOWN = 0;
+        // Time of the bootstat's marking of 1st boot after the last factory reset.
+        // Logged from bootstat.
+        FACTORY_RESET_RESET_TIME = 1;
+        // The time when bootstat records FACTORY_RESET_* events. This is close to
+        // BOOT_COMPLETE time for the current bootup.
+        // Logged from bootstat.
+        FACTORY_RESET_CURRENT_TIME = 2;
+        // DUplicate of FACTORY_RESET_RESET_TIME added for debugging purpose.
+        // Logged from bootstat.
+        FACTORY_RESET_RECORD_VALUE = 3;
+    }
+
+    // Type of the event.
+    optional UtcTimeEvent event = 1;
+    // UTC time for the event.
+    optional int64 utc_time_secs = 2;
+}
+
+/**
+ * Boot time events representing specific error code during bootup.
+ * Meaning of error code can be different per each event type.
+ *
+ * Logged from: bootstat and various system server components. Check each enums for details.
+ */
+message BootTimeEventErrorCode {
+    enum ErrorCodeEvent {
+        UNKNOWN = 0;
+        // Linux error code for time() call to get the current UTC time.
+        // Logged from bootstat.
+        FACTORY_RESET_CURRENT_TIME_FAILURE = 1;
+        // Represents UmountStat before the reboot for the current boot up. Error codes defined
+        // as UMOUNT_STAT_* from init/reboot.cpp.
+        // Logged from f/b/services/.../BootReceiver.java.
+        SHUTDOWN_UMOUNT_STAT = 2;
+        // Reprepsents fie system mounting error code for the current boot. Error codes defined
+        // as combination of FsStatFlags from system/core/fs_mgr/fs_mgr.cpp.
+        // Logged from f/b/services/.../BootReceiver.java.
+        FS_MGR_FS_STAT = 3;
+    }
+
+    // Type of the event.
+    optional ErrorCodeEvent event = 1;
+    // error code defined per each event type.
+    // For example, this can have a value of FsStatFlags.FS_STAT_FULL_MOUNT_FAILED for the event of
+    // FS_MGR_FS_STAT.
+    optional int32 error_code = 2;
+}
+
 //////////////////////////////////////////////////////////////////////
 // Pulled atoms below this line //
 //////////////////////////////////////////////////////////////////////
@@ -4725,36 +4932,69 @@
 }
 
 message AggStats {
-    optional int64 min = 1;
+    // These are all in byte resolution.
+    optional int64 min = 1 [deprecated = true];
+    optional int64 average = 2 [deprecated = true];
+    optional int64 max = 3 [deprecated = true];
 
-    optional int64 average = 2;
-
-    optional int64 max = 3;
+    // These are all in kilobyte resolution. Can fit in int32, so smaller on the wire than the above
+    // int64 fields.
+    optional int32 mean_kb = 4;
+    optional int32 max_kb = 5;
 }
 
+// A reduced subset of process states; reducing the number of possible states allows more
+// aggressive device-side aggregation of statistics and hence reduces metric upload size.
+enum ProcessStateAggregated {
+    PROCESS_STATE_UNKNOWN = 0;
+    // Persistent system process.
+    PROCESS_STATE_PERSISTENT = 1;
+    // Top activity; actually any visible activity.
+    PROCESS_STATE_TOP = 2;
+    // Process binding to top or a foreground service.
+    PROCESS_STATE_BOUND_TOP_OR_FGS = 3;
+    // Processing running a foreground service.
+    PROCESS_STATE_FGS = 4;
+    // Important foreground process (ime, wallpaper, etc).
+    PROCESS_STATE_IMPORTANT_FOREGROUND = 5;
+    // Important background process.
+    PROCESS_STATE_BACKGROUND = 6;
+    // Process running a receiver.
+    PROCESS_STATE_RECEIVER = 7;
+    // All kinds of cached processes.
+    PROCESS_STATE_CACHED = 8;
+}
+
+// Next tag: 13
 message ProcessStatsStateProto {
     optional android.service.procstats.ScreenState screen_state = 1;
 
-    optional android.service.procstats.MemoryState memory_state = 2;
+    optional android.service.procstats.MemoryState memory_state = 2 [deprecated = true];
 
     // this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
     // and not frameworks/base/core/java/android/app/ActivityManager.java
-    optional android.service.procstats.ProcessState process_state = 3;
+    optional android.service.procstats.ProcessState process_state = 3 [deprecated = true];
+
+    optional ProcessStateAggregated process_state_aggregated = 10;
 
     // Millisecond uptime duration spent in this state
-    optional int64 duration_millis = 4;
+    optional int64 duration_millis = 4 [deprecated = true];
+    // Same as above, but with minute resolution so it fits into an int32.
+    optional int32 duration_minutes = 11;
 
     // Millisecond elapsed realtime duration spent in this state
-    optional int64 realtime_duration_millis = 9;
+    optional int64 realtime_duration_millis = 9 [deprecated = true];
+    // Same as above, but with minute resolution so it fits into an int32.
+    optional int32 realtime_duration_minutes = 12;
 
     // # of samples taken
     optional int32 sample_size = 5;
 
     // PSS is memory reserved for this process
-    optional AggStats pss = 6;
+    optional AggStats pss = 6 [deprecated = true];
 
     // USS is memory shared between processes, divided evenly for accounting
-    optional AggStats uss = 7;
+    optional AggStats uss = 7 [deprecated = true];
 
     // RSS is memory resident for this process
     optional AggStats rss = 8;
@@ -4779,7 +5019,7 @@
         // PSS stats during cached kill
         optional AggStats cached_pss = 3;
     }
-    optional Kill kill = 3;
+    optional Kill kill = 3 [deprecated = true];
 
     // Time and memory spent in various states.
     repeated ProcessStatsStateProto states = 5;
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
index ef59c92..3ad21e0 100644
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -28,10 +28,14 @@
 StateTracker::StateTracker(const int32_t atomId, const util::StateAtomFieldOptions& stateAtomInfo)
     : mAtomId(atomId), mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) {
     // create matcher for each primary field
-    // TODO(tsaichristine): b/142108433 handle when primary field is first uid in chain
-    for (const auto& primary : stateAtomInfo.primaryFields) {
-        Matcher matcher = getSimpleMatcher(atomId, primary);
-        mPrimaryFields.push_back(matcher);
+    for (const auto& primaryField : stateAtomInfo.primaryFields) {
+        if (primaryField == util::FIRST_UID_IN_CHAIN) {
+            Matcher matcher = getFirstUidMatcher(atomId);
+            mPrimaryFields.push_back(matcher);
+        } else {
+            Matcher matcher = getSimpleMatcher(atomId, primaryField);
+            mPrimaryFields.push_back(matcher);
+        }
     }
 
     // TODO(tsaichristine): b/142108433 set default state, reset state, and nesting
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
index 7453370..70f1627 100644
--- a/cmds/statsd/src/state/StateTracker.h
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -72,7 +72,7 @@
 
     int32_t mDefaultState = kStateUnknown;
 
-    int32_t mResetState;
+    int32_t mResetState = kStateUnknown;
 
     // Maps primary key to state value info
     std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap;
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index 26a3733..84aaa54 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -76,6 +76,23 @@
     return event;
 }
 
+// State with first uid in attribution chain as primary field - WakelockStateChanged
+std::shared_ptr<LogEvent> buildPartialWakelockEvent(int uid, const std::string& tag, bool acquire) {
+    std::vector<AttributionNodeInternal> chain;
+    chain.push_back(AttributionNodeInternal());
+    AttributionNodeInternal& attr = chain.back();
+    attr.set_uid(uid);
+
+    std::shared_ptr<LogEvent> event =
+            std::make_shared<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, 1000 /* timestamp */);
+    event->write(chain);
+    event->write((int32_t)1);  // PARTIAL_WAKE_LOCK
+    event->write(tag);
+    event->write(acquire ? 1 : 0);
+    event->init();
+    return event;
+}
+
 // State with multiple primary fields - OverlayStateChanged
 std::shared_ptr<LogEvent> buildOverlayEvent(int uid, const std::string& packageName, int state) {
     std::shared_ptr<LogEvent> event =
@@ -134,6 +151,39 @@
     key->addValue(FieldValue(field1, value1));
     key->addValue(FieldValue(field2, value2));
 }
+
+void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key) {
+    int pos1[] = {1, 1, 1};
+    int pos3[] = {2, 0, 0};
+    int pos4[] = {3, 0, 0};
+
+    Field field1(10 /* atom id */, pos1, 2 /* depth */);
+
+    Field field3(10 /* atom id */, pos3, 0 /* depth */);
+    Field field4(10 /* atom id */, pos4, 0 /* depth */);
+
+    Value value1((int32_t)uid);
+    Value value3((int32_t)1 /*partial*/);
+    Value value4(tag);
+
+    key->addValue(FieldValue(field1, value1));
+    key->addValue(FieldValue(field3, value3));
+    key->addValue(FieldValue(field4, value4));
+}
+
+void getPartialWakelockKey(int uid, HashableDimensionKey* key) {
+    int pos1[] = {1, 1, 1};
+    int pos3[] = {2, 0, 0};
+
+    Field field1(10 /* atom id */, pos1, 2 /* depth */);
+    Field field3(10 /* atom id */, pos3, 0 /* depth */);
+
+    Value value1((int32_t)uid);
+    Value value3((int32_t)1 /*partial*/);
+
+    key->addValue(FieldValue(field1, value1));
+    key->addValue(FieldValue(field3, value3));
+}
 // END: get primary key functions
 
 TEST(StateListenerTest, TestStateListenerWeakPointer) {
@@ -247,7 +297,8 @@
 
     // check StateTracker was updated by querying for state
     HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
-    EXPECT_EQ(2, getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, queryKey));
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+              getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, queryKey));
 }
 
 /**
@@ -272,7 +323,46 @@
     // check StateTracker was updated by querying for state
     HashableDimensionKey queryKey;
     getUidProcessKey(1000 /* uid */, &queryKey);
-    EXPECT_EQ(1002, getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey));
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
+              getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey));
+}
+
+TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) {
+    sp<TestStateListener> listener1 = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(android::util::WAKELOCK_STATE_CHANGED, listener1);
+
+    // Log event.
+    std::shared_ptr<LogEvent> event =
+            buildPartialWakelockEvent(1001 /* uid */, "tag1", false /* acquire */);
+    mgr.onLogEvent(*event);
+
+    EXPECT_EQ(1, mgr.getStateTrackersCount());
+    EXPECT_EQ(1, mgr.getListenersCount(android::util::WAKELOCK_STATE_CHANGED));
+
+    // Check listener was updated.
+    EXPECT_EQ(1, listener1->updates.size());
+    EXPECT_EQ(3, listener1->updates[0].mKey.getValues().size());
+    EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value);
+    EXPECT_EQ("tag1", listener1->updates[0].mKey.getValues()[2].mValue.str_value);
+    EXPECT_EQ(WakelockStateChanged::RELEASE, listener1->updates[0].mState);
+
+    // Check StateTracker was updated by querying for state.
+    HashableDimensionKey queryKey;
+    getPartialWakelockKey(1001 /* uid */, "tag1", &queryKey);
+    EXPECT_EQ(WakelockStateChanged::RELEASE,
+              getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey));
+
+    // No state stored for this query key.
+    HashableDimensionKey queryKey2;
+    getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2);
+    EXPECT_EQ(-1, getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey2));
+
+    // Partial query fails.
+    HashableDimensionKey queryKey3;
+    getPartialWakelockKey(1001 /* uid */, &queryKey3);
+    EXPECT_EQ(-1, getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey3));
 }
 
 /**
@@ -297,7 +387,8 @@
     // check StateTracker was updated by querying for state
     HashableDimensionKey queryKey;
     getOverlayKey(1000 /* uid */, "package1", &queryKey);
-    EXPECT_EQ(1, getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey));
+    EXPECT_EQ(OverlayStateChanged::ENTERED,
+              getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey));
 }
 
 /**
@@ -326,10 +417,12 @@
     sp<TestStateListener> listener1 = new TestStateListener();
     sp<TestStateListener> listener2 = new TestStateListener();
     sp<TestStateListener> listener3 = new TestStateListener();
+    sp<TestStateListener> listener4 = new TestStateListener();
     StateManager mgr;
     mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
     mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener2);
     mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener3);
+    mgr.registerListener(android::util::WAKELOCK_STATE_CHANGED, listener4);
 
     std::shared_ptr<LogEvent> event1 = buildUidProcessEvent(
             1000,
@@ -346,8 +439,12 @@
             android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
     std::shared_ptr<LogEvent> event5 =
             buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-    std::shared_ptr<LogEvent> event6 = buildOverlayEvent(1000, "package1", 1);
-    std::shared_ptr<LogEvent> event7 = buildOverlayEvent(1000, "package2", 2);
+    std::shared_ptr<LogEvent> event6 =
+            buildOverlayEvent(1000, "package1", OverlayStateChanged::ENTERED);
+    std::shared_ptr<LogEvent> event7 =
+            buildOverlayEvent(1000, "package2", OverlayStateChanged::EXITED);
+    std::shared_ptr<LogEvent> event8 = buildPartialWakelockEvent(1005, "tag1", true);
+    std::shared_ptr<LogEvent> event9 = buildPartialWakelockEvent(1005, "tag2", false);
 
     mgr.onLogEvent(*event1);
     mgr.onLogEvent(*event2);
@@ -356,11 +453,14 @@
     mgr.onLogEvent(*event5);
     mgr.onLogEvent(*event6);
     mgr.onLogEvent(*event7);
+    mgr.onLogEvent(*event8);
+    mgr.onLogEvent(*event9);
 
     // Query for UidProcessState of uid 1001
     HashableDimensionKey queryKey1;
     getUidProcessKey(1001, &queryKey1);
-    EXPECT_EQ(1003, getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
+              getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
 
     // Query for UidProcessState of uid 1004 - not in state map
     HashableDimensionKey queryKey2;
@@ -370,15 +470,30 @@
 
     // Query for UidProcessState of uid 1001 - after change in state
     mgr.onLogEvent(*event4);
-    EXPECT_EQ(1002, getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
+              getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
 
     // Query for ScreenState
-    EXPECT_EQ(2, getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+              getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
 
     // Query for OverlayState of uid 1000, package name "package2"
     HashableDimensionKey queryKey3;
     getOverlayKey(1000, "package2", &queryKey3);
-    EXPECT_EQ(2, getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey3));
+    EXPECT_EQ(OverlayStateChanged::EXITED,
+              getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey3));
+
+    // Query for WakelockState of uid 1005, tag 2
+    HashableDimensionKey queryKey4;
+    getPartialWakelockKey(1005, "tag2", &queryKey4);
+    EXPECT_EQ(WakelockStateChanged::RELEASE,
+              getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey4));
+
+    // Query for WakelockState of uid 1005, tag 1
+    HashableDimensionKey queryKey5;
+    getPartialWakelockKey(1005, "tag1", &queryKey5);
+    EXPECT_EQ(WakelockStateChanged::ACQUIRE,
+              getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey5));
 }
 
 }  // namespace statsd
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index c0fee6e..e46840c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -1547,6 +1547,34 @@
             void onShowModeChanged(@NonNull SoftKeyboardController controller,
                     @SoftKeyboardShowMode int showMode);
         }
+
+        /**
+         * Switches the current IME for the user for whom the service is enabled. The change will
+         * persist until the current IME is explicitly changed again, and may persist beyond the
+         * life cycle of the requesting service.
+         *
+         * @param imeId The ID of the input method to make current. This IME must be installed and
+         *              enabled.
+         * @return {@code true} if the current input method was successfully switched to the input
+         *         method by {@code imeId},
+         *         {@code false} if the input method specified is not installed, not enabled, or
+         *         otherwise not available to become the current IME
+         *
+         * @see android.view.inputmethod.InputMethodInfo#getId()
+         */
+        public boolean switchToInputMethod(@NonNull String imeId) {
+            final IAccessibilityServiceConnection connection =
+                    AccessibilityInteractionClient.getInstance().getConnection(
+                            mService.mConnectionId);
+            if (connection != null) {
+                try {
+                    return connection.switchToInputMethod(imeId);
+                } catch (RemoteException re) {
+                    throw new RuntimeException(re);
+                }
+            }
+            return false;
+        }
     }
 
     /**
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 4841781..656f87f 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -91,6 +91,8 @@
 
     void setSoftKeyboardCallbackEnabled(boolean enabled);
 
+    boolean switchToInputMethod(String imeId);
+
     boolean isAccessibilityButtonAvailable();
 
     void sendGesture(int sequence, in ParceledListSlice gestureSteps);
diff --git a/core/java/android/annotation/SystemService.java b/core/java/android/annotation/SystemService.java
index 0c5d15e..c05c1ba 100644
--- a/core/java/android/annotation/SystemService.java
+++ b/core/java/android/annotation/SystemService.java
@@ -19,14 +19,12 @@
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
-import android.content.Context;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 /**
  * Description of a system service available through
- * {@link Context#getSystemService(Class)}. This is used to auto-generate
+ * {@link android.content.Context#getSystemService(Class)}. This is used to auto-generate
  * documentation explaining how to obtain a reference to the service.
  *
  * @hide
@@ -36,9 +34,9 @@
 public @interface SystemService {
     /**
      * The string name of the system service that can be passed to
-     * {@link Context#getSystemService(String)}.
+     * {@link android.content.Context#getSystemService(String)}.
      *
-     * @see Context#getSystemServiceName(Class)
+     * @see android.content.Context#getSystemServiceName(Class)
      */
     String value();
 }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4a8e4e2..a11f41f 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -726,7 +726,17 @@
     /** @hide Capture the device's display contents and/or audio */
     @UnsupportedAppUsage
     public static final int OP_PROJECT_MEDIA = 46;
-    /** @hide Activate a VPN connection without user intervention. */
+    /**
+     * Start (without additional user intervention) a VPN connection, as used by {@link
+     * android.net.VpnService} along with as Platform VPN connections, as used by {@link
+     * android.net.VpnManager}
+     *
+     * <p>This appop is granted to apps that have already been given user consent to start
+     * VpnService based VPN connections. As this is a superset of OP_ACTIVATE_PLATFORM_VPN, this
+     * appop also allows the starting of Platform VPNs.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
     public static final int OP_ACTIVATE_VPN = 47;
     /** @hide Access the WallpaperManagerAPI to write wallpapers. */
@@ -852,10 +862,21 @@
     public static final int OP_MANAGE_EXTERNAL_STORAGE = 92;
     /** @hide Communicate cross-profile within the same profile group. */
     public static final int OP_INTERACT_ACROSS_PROFILES = 93;
+    /**
+     * Start (without additional user intervention) a Platform VPN connection, as used by {@link
+     * android.net.VpnManager}
+     *
+     * <p>This appop is granted to apps that have already been given user consent to start Platform
+     * VPN connections. This appop is insufficient to start VpnService based VPNs; OP_ACTIVATE_VPN
+     * is needed for that.
+     *
+     * @hide
+     */
+    public static final int OP_ACTIVATE_PLATFORM_VPN = 94;
 
     /** @hide */
     @UnsupportedAppUsage
-    public static final int _NUM_OP = 94;
+    public static final int _NUM_OP = 95;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1149,6 +1170,8 @@
     /** @hide Communicate cross-profile within the same profile group. */
     @SystemApi
     public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles";
+    /** @hide Start Platform VPN without user intervention */
+    public static final String OPSTR_ACTIVATE_PLATFORM_VPN = "android:activate_platform_vpn";
 
 
     /** {@link #sAppOpsToNote} not initialized yet for this op */
@@ -1333,6 +1356,7 @@
             OP_QUERY_ALL_PACKAGES,              // QUERY_ALL_PACKAGES
             OP_MANAGE_EXTERNAL_STORAGE,         // MANAGE_EXTERNAL_STORAGE
             OP_INTERACT_ACROSS_PROFILES,        //INTERACT_ACROSS_PROFILES
+            OP_ACTIVATE_PLATFORM_VPN,           // ACTIVATE_PLATFORM_VPN
     };
 
     /**
@@ -1433,6 +1457,7 @@
             OPSTR_QUERY_ALL_PACKAGES,
             OPSTR_MANAGE_EXTERNAL_STORAGE,
             OPSTR_INTERACT_ACROSS_PROFILES,
+            OPSTR_ACTIVATE_PLATFORM_VPN,
     };
 
     /**
@@ -1533,7 +1558,8 @@
             "ACCESS_MEDIA_LOCATION",
             "QUERY_ALL_PACKAGES",
             "MANAGE_EXTERNAL_STORAGE",
-            "INTERACT_ACROSS_PROFILES"
+            "INTERACT_ACROSS_PROFILES",
+            "ACTIVATE_PLATFORM_VPN",
     };
 
     /**
@@ -1636,6 +1662,7 @@
             null, // no permission for OP_QUERY_ALL_PACKAGES
             Manifest.permission.MANAGE_EXTERNAL_STORAGE,
             android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+            null, // no permission for OP_ACTIVATE_PLATFORM_VPN
     };
 
     /**
@@ -1738,6 +1765,7 @@
             null, // QUERY_ALL_PACKAGES
             null, // MANAGE_EXTERNAL_STORAGE
             null, // INTERACT_ACROSS_PROFILES
+            null, // ACTIVATE_PLATFORM_VPN
     };
 
     /**
@@ -1839,6 +1867,7 @@
             false, // QUERY_ALL_PACKAGES
             false, // MANAGE_EXTERNAL_STORAGE
             false, // INTERACT_ACROSS_PROFILES
+            false, // ACTIVATE_PLATFORM_VPN
     };
 
     /**
@@ -1939,6 +1968,7 @@
             AppOpsManager.MODE_DEFAULT, // QUERY_ALL_PACKAGES
             AppOpsManager.MODE_DEFAULT, // MANAGE_EXTERNAL_STORAGE
             AppOpsManager.MODE_DEFAULT, // INTERACT_ACROSS_PROFILES
+            AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN
     };
 
     /**
@@ -2043,6 +2073,7 @@
             false, // QUERY_ALL_PACKAGES
             false, // MANAGE_EXTERNAL_STORAGE
             false, // INTERACT_ACROSS_PROFILES
+            false, // ACTIVATE_PLATFORM_VPN
     };
 
     /**
diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
index d993ec1..3febf71 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -256,10 +256,10 @@
     };
 
     @DataClass.Generated(
-            time = 1578321462996L,
+            time = 1578516519372L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
-            inputSignatures = "private final @android.annotation.IntRange(from=0L, to=93L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.IntRange(from=0L, to=94L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index aa11598..d23754e 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -482,19 +482,6 @@
             }
         }
 
-        if (key.mOverlayDirs != null) {
-            for (final String idmapPath : key.mOverlayDirs) {
-                try {
-                    builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/,
-                            true /*overlay*/));
-                } catch (IOException e) {
-                    Log.w(TAG, "failed to add overlay path " + idmapPath);
-
-                    // continue.
-                }
-            }
-        }
-
         if (key.mLibDirs != null) {
             for (final String libDir : key.mLibDirs) {
                 if (libDir.endsWith(".apk")) {
@@ -513,6 +500,19 @@
             }
         }
 
+        if (key.mOverlayDirs != null) {
+            for (final String idmapPath : key.mOverlayDirs) {
+                try {
+                    builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/,
+                            true /*overlay*/));
+                } catch (IOException e) {
+                    Log.w(TAG, "failed to add overlay path " + idmapPath);
+
+                    // continue.
+                }
+            }
+        }
+
         return builder.build();
     }
 
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index ca3d0d7..a9be9ea 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -170,6 +170,7 @@
 import android.service.persistentdata.PersistentDataBlockManager;
 import android.service.vr.IVrManager;
 import android.telecom.TelecomManager;
+import android.telephony.MmsManager;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.TelephonyRegistryManager;
 import android.util.ArrayMap;
@@ -345,6 +346,14 @@
             }
         });
 
+        registerService(Context.NETWORK_STACK_SERVICE, IBinder.class,
+                new StaticServiceFetcher<IBinder>() {
+                    @Override
+                    public IBinder createService() {
+                        return ServiceManager.getService(Context.NETWORK_STACK_SERVICE);
+                    }
+                });
+
         registerService(Context.TETHERING_SERVICE, TetheringManager.class,
                 new CachedServiceFetcher<TetheringManager>() {
             @Override
@@ -631,6 +640,13 @@
                 return new TelecomManager(ctx.getOuterContext());
             }});
 
+        registerService(Context.MMS_SERVICE, MmsManager.class,
+                new CachedServiceFetcher<MmsManager>() {
+                    @Override
+                    public MmsManager createService(ContextImpl ctx) {
+                        return new MmsManager(ctx.getOuterContext());
+                    }});
+
         registerService(Context.UI_MODE_SERVICE, UiModeManager.class,
                 new CachedServiceFetcher<UiModeManager>() {
             @Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 2aac94c..69640b8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -398,6 +398,42 @@
             "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
 
     /**
+     * Activity action: Starts the provisioning flow which sets up a financed device.
+     *
+     * <p>During financed device provisioning, a device admin app is downloaded and set as the owner
+     * of the device. A device owner has full control over the device. The device owner can not be
+     * modified by the user.
+     *
+     * <p>A typical use case would be a device that is bought from the reseller through financing
+     * program.
+     *
+     * <p>An intent with this action can be sent only on an unprovisioned device.
+     *
+     * <p>Unlike {@link #ACTION_PROVISION_MANAGED_DEVICE}, the provisioning message can only be sent
+     * by a privileged app with the permission
+     * {@link android.Manifest.permission#DISPATCH_PROVISIONING_MESSAGE}.
+     *
+     * <p>The provisioning intent contains the following properties:
+     * <ul>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_SUPPORT_URL}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_ORGANIZATION_NAME}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
+     * </ul>
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @SystemApi
+    public static final String ACTION_PROVISION_FINANCED_DEVICE =
+            "android.app.action.PROVISION_FINANCED_DEVICE";
+
+    /**
      * Activity action: Starts the provisioning flow which sets up a managed device.
      * Must be started with {@link android.app.Activity#startActivityForResult(Intent, int)}.
      *
@@ -864,6 +900,7 @@
      * The name is displayed only during provisioning.
      *
      * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
+     * or {@link #ACTION_PROVISION_FINANCED_DEVICE}
      *
      * @hide
      */
@@ -876,6 +913,7 @@
      * during provisioning. If the url is not HTTPS, an error will be shown.
      *
      * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
+     * or {@link #ACTION_PROVISION_FINANCED_DEVICE}
      *
      * @hide
      */
@@ -888,6 +926,7 @@
      * as the app label of the package.
      *
      * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
+     * or {@link #ACTION_PROVISION_FINANCED_DEVICE}
      *
      * @hide
      */
@@ -912,6 +951,7 @@
      * {@link android.content.ClipData} of the intent too.
      *
      * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
+     * or {@link #ACTION_PROVISION_FINANCED_DEVICE}
      *
      * @hide
      */
@@ -1371,6 +1411,16 @@
             = "android.app.action.DEVICE_OWNER_CHANGED";
 
     /**
+     * Broadcast action: sent when the factory reset protection (FRP) policy is changed.
+     *
+     * @see #setFactoryResetProtectionPolicy
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED =
+            "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
+
+    /**
      * The ComponentName of the administrator component.
      *
      * @see #ACTION_ADD_DEVICE_ADMIN
@@ -4289,6 +4339,60 @@
     }
 
     /**
+     * Callable by device owner or profile owner of an organization-owned device, to set a
+     * factory reset protection (FRP) policy. When a new policy is set, the system
+     * notifies the FRP management agent of a policy change by broadcasting
+     * {@code ACTION_RESET_PROTECTION_POLICY_CHANGED}.
+     *
+     * @param admin  Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param policy the new FRP policy, or {@code null} to clear the current policy.
+     * @throws SecurityException if {@code admin} is not a device owner or a profile owner of
+     *                           an organization-owned device.
+     * @throws UnsupportedOperationException if factory reset protection is not
+     *                           supported on the device.
+     */
+    public void setFactoryResetProtectionPolicy(@NonNull ComponentName admin,
+            @Nullable FactoryResetProtectionPolicy policy) {
+        throwIfParentInstance("setFactoryResetProtectionPolicy");
+        if (mService != null) {
+            try {
+                mService.setFactoryResetProtectionPolicy(admin, policy);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Callable by device owner or profile owner of an organization-owned device, to retrieve
+     * the current factory reset protection (FRP) policy set previously by
+     * {@link #setFactoryResetProtectionPolicy}.
+     * <p>
+     * This method can also be called by the FRP management agent on device, in which case,
+     * it can pass {@code null} as the ComponentName.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with or
+     *              {@code null} if called by the FRP management agent on device.
+     * @return The current FRP policy object or {@code null} if no policy is set.
+     * @throws SecurityException if {@code admin} is not a device owner, a profile owner of
+     *                           an organization-owned device or the FRP management agent.
+     * @throws UnsupportedOperationException if factory reset protection is not
+     *                           supported on the device.
+     */
+    public @Nullable FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(
+            @Nullable ComponentName admin) {
+        throwIfParentInstance("getFactoryResetProtectionPolicy");
+        if (mService != null) {
+            try {
+                return mService.getFactoryResetProtectionPolicy(admin);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return null;
+    }
+
+    /**
      * Called by an application that is administering the device to set the
      * global proxy and exclusion list.
      * <p>
diff --git a/media/java/android/media/RouteDiscoveryRequest.aidl b/core/java/android/app/admin/FactoryResetProtectionPolicy.aidl
similarity index 81%
copy from media/java/android/media/RouteDiscoveryRequest.aidl
copy to core/java/android/app/admin/FactoryResetProtectionPolicy.aidl
index 744f656..72e639a 100644
--- a/media/java/android/media/RouteDiscoveryRequest.aidl
+++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.app.admin;
 
-parcelable RouteDiscoveryRequest;
+parcelable FactoryResetProtectionPolicy;
diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
new file mode 100644
index 0000000..ed74779
--- /dev/null
+++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
@@ -0,0 +1,237 @@
+/*
+ * 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.app.admin;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The factory reset protection policy determines which accounts can unlock a device that
+ * has gone through untrusted factory reset.
+ * <p>
+ * Only a device owner or profile owner of an organization-owned device can set a factory
+ * reset protection policy for the device by calling the {@code DevicePolicyManager} method
+ * {@link DevicePolicyManager#setFactoryResetProtectionPolicy(ComponentName,
+ * FactoryResetProtectionPolicy)}}.
+ *
+ * @see DevicePolicyManager#setFactoryResetProtectionPolicy
+ * @see DevicePolicyManager#getFactoryResetProtectionPolicy
+ */
+public final class FactoryResetProtectionPolicy implements Parcelable {
+
+    private static final String LOG_TAG = "FactoryResetProtectionPolicy";
+
+    private static final String KEY_FACTORY_RESET_PROTECTION_ACCOUNT =
+            "factory_reset_protection_account";
+    private static final String KEY_FACTORY_RESET_PROTECTION_DISABLED =
+            "factory_reset_protection_disabled";
+    private static final String ATTR_VALUE = "value";
+
+    private final List<String> mFactoryResetProtectionAccounts;
+    private final boolean mFactoryResetProtectionDisabled;
+
+    private FactoryResetProtectionPolicy(List<String> factoryResetProtectionAccounts,
+            boolean factoryResetProtectionDisabled) {
+        mFactoryResetProtectionAccounts = factoryResetProtectionAccounts;
+        mFactoryResetProtectionDisabled = factoryResetProtectionDisabled;
+    }
+
+    /**
+     * Get the list of accounts that can provision a device which has been factory reset.
+     */
+    public @NonNull List<String> getFactoryResetProtectionAccounts() {
+        return mFactoryResetProtectionAccounts;
+    }
+
+    /**
+     * Return whether factory reset protection for the device is disabled or not.
+     */
+    public boolean isFactoryResetProtectionDisabled() {
+        return mFactoryResetProtectionDisabled;
+    }
+
+    /**
+     * Builder class for {@link FactoryResetProtectionPolicy} objects.
+     */
+    public static class Builder {
+        private List<String> mFactoryResetProtectionAccounts;
+        private boolean mFactoryResetProtectionDisabled;
+
+        /**
+         * Initialize a new Builder to construct a {@link FactoryResetProtectionPolicy}.
+         */
+        public Builder() {
+        };
+
+        /**
+         * Sets which accounts can unlock a device that has been factory reset.
+         * <p>
+         * Once set, the consumer unlock flow will be disabled and only accounts in this list
+         * can unlock factory reset protection after untrusted factory reset.
+         * <p>
+         * It's up to the FRP management agent to interpret the {@code String} as account it
+         * supports. Please consult their relevant documentation for details.
+         *
+         * @param factoryResetProtectionAccounts list of accounts.
+         * @return the same Builder instance.
+         */
+        @NonNull
+        public Builder setFactoryResetProtectionAccounts(
+                @NonNull List<String> factoryResetProtectionAccounts) {
+            mFactoryResetProtectionAccounts = new ArrayList<>(factoryResetProtectionAccounts);
+            return this;
+        }
+
+        /**
+         * Sets whether factory reset protection is disabled or not.
+         * <p>
+         * Once disabled, factory reset protection will not kick in all together when the device
+         * goes through untrusted factory reset. This applies to both the consumer unlock flow and
+         * the admin account overrides via {@link #setFactoryResetProtectionAccounts}
+         *
+         * @param factoryResetProtectionDisabled Whether the policy is disabled or not.
+         * @return the same Builder instance.
+         */
+        @NonNull
+        public Builder setFactoryResetProtectionDisabled(boolean factoryResetProtectionDisabled) {
+            mFactoryResetProtectionDisabled = factoryResetProtectionDisabled;
+            return this;
+        }
+
+        /**
+         * Combines all of the attributes that have been set on this {@code Builder}
+         *
+         * @return a new {@link FactoryResetProtectionPolicy} object.
+         */
+        @NonNull
+        public FactoryResetProtectionPolicy build() {
+            return new FactoryResetProtectionPolicy(mFactoryResetProtectionAccounts,
+                    mFactoryResetProtectionDisabled);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "FactoryResetProtectionPolicy{"
+                + "mFactoryResetProtectionAccounts=" + mFactoryResetProtectionAccounts
+                + ", mFactoryResetProtectionDisabled=" + mFactoryResetProtectionDisabled
+                + '}';
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, @Nullable int flags) {
+        int accountsCount = mFactoryResetProtectionAccounts.size();
+        dest.writeInt(accountsCount);
+        for (String account: mFactoryResetProtectionAccounts) {
+            dest.writeString(account);
+        }
+        dest.writeBoolean(mFactoryResetProtectionDisabled);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Creator<FactoryResetProtectionPolicy> CREATOR =
+            new Creator<FactoryResetProtectionPolicy>() {
+
+                @Override
+                public FactoryResetProtectionPolicy createFromParcel(Parcel in) {
+                    List<String> factoryResetProtectionAccounts = new ArrayList<>();
+                    int accountsCount = in.readInt();
+                    for (int i = 0; i < accountsCount; i++) {
+                        factoryResetProtectionAccounts.add(in.readString());
+                    }
+                    boolean factoryResetProtectionDisabled = in.readBoolean();
+
+                    return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts,
+                            factoryResetProtectionDisabled);
+                }
+
+                @Override
+                public FactoryResetProtectionPolicy[] newArray(int size) {
+                    return new FactoryResetProtectionPolicy[size];
+                }
+    };
+
+    /**
+     * Restore a previously saved FactoryResetProtectionPolicy from XML.
+     * <p>
+     * No validation is required on the reconstructed policy since the XML was previously
+     * created by the system server from a validated policy.
+     * @hide
+     */
+    @Nullable
+    public static FactoryResetProtectionPolicy readFromXml(@NonNull XmlPullParser parser) {
+        try {
+            boolean factoryResetProtectionDisabled = Boolean.parseBoolean(
+                    parser.getAttributeValue(null, KEY_FACTORY_RESET_PROTECTION_DISABLED));
+
+            List<String> factoryResetProtectionAccounts = new ArrayList<>();
+            int outerDepth = parser.getDepth();
+            int type;
+            while ((type = parser.next()) != END_DOCUMENT
+                    && (type != END_TAG || parser.getDepth() > outerDepth)) {
+                if (type == END_TAG || type == TEXT) {
+                    continue;
+                }
+                if (!parser.getName().equals(KEY_FACTORY_RESET_PROTECTION_ACCOUNT)) {
+                    continue;
+                }
+                factoryResetProtectionAccounts.add(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            }
+
+            return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts,
+                    factoryResetProtectionDisabled);
+        } catch (XmlPullParserException | IOException e) {
+            Log.w(LOG_TAG, "Reading from xml failed", e);
+        }
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    public void writeToXml(@NonNull XmlSerializer out) throws IOException {
+        out.attribute(null, KEY_FACTORY_RESET_PROTECTION_DISABLED,
+                Boolean.toString(mFactoryResetProtectionDisabled));
+        for (String account : mFactoryResetProtectionAccounts) {
+            out.startTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT);
+            out.attribute(null, ATTR_VALUE, account);
+            out.endTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT);
+        }
+    }
+
+}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 3eec46b..21c9eb5 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -24,6 +24,7 @@
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
 import android.app.admin.PasswordMetrics;
+import android.app.admin.FactoryResetProtectionPolicy;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -104,6 +105,9 @@
 
     void wipeDataWithReason(int flags, String wipeReasonForUser, boolean parent);
 
+    void setFactoryResetProtectionPolicy(in ComponentName who, in FactoryResetProtectionPolicy policy);
+    FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(in ComponentName who);
+
     ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList);
     ComponentName getGlobalProxyAdmin(int userHandle);
     void setRecommendedGlobalProxy(in ComponentName admin, in ProxyInfo proxyInfo);
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 4346d97..9c4a8f4 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -16,7 +16,10 @@
 
 package android.app.usage;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.app.usage.NetworkStats.Bucket;
@@ -27,6 +30,9 @@
 import android.net.INetworkStatsService;
 import android.net.NetworkIdentity;
 import android.net.NetworkTemplate;
+import android.net.netstats.provider.AbstractNetworkStatsProvider;
+import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProviderWrapper;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
@@ -519,6 +525,34 @@
         private DataUsageRequest request;
     }
 
+    /**
+     * Registers a custom provider of {@link android.net.NetworkStats} to combine the network
+     * statistics that cannot be seen by the kernel to system. To unregister, invoke
+     * {@link NetworkStatsProviderCallback#unregister()}.
+     *
+     * @param tag a human readable identifier of the custom network stats provider.
+     * @param provider a custom implementation of {@link AbstractNetworkStatsProvider} that needs to
+     *                 be registered to the system.
+     * @return a {@link NetworkStatsProviderCallback}, which can be used to report events to the
+     *         system.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    @NonNull public NetworkStatsProviderCallback registerNetworkStatsProvider(
+            @NonNull String tag,
+            @NonNull AbstractNetworkStatsProvider provider) {
+        try {
+            final NetworkStatsProviderWrapper wrapper = new NetworkStatsProviderWrapper(provider);
+            return new NetworkStatsProviderCallback(
+                    mService.registerNetworkStatsProvider(tag, wrapper));
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+        // Unreachable code, but compiler doesn't know about it.
+        return null;
+    }
+
     private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
         final NetworkTemplate template;
         switch (networkType) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 44b2df6..9ef9574 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3416,6 +3416,7 @@
             TELEPHONY_SUBSCRIPTION_SERVICE,
             CARRIER_CONFIG_SERVICE,
             EUICC_SERVICE,
+            MMS_SERVICE,
             TELECOM_SERVICE,
             CLIPBOARD_SERVICE,
             INPUT_METHOD_SERVICE,
@@ -3612,6 +3613,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
@@ -3950,10 +3953,12 @@
 
     /**
      * Use with {@link android.os.ServiceManager.getService()} to retrieve a
-     * {@link NetworkStackClient} IBinder for communicating with the network stack
+     * {@link INetworkStackConnector} IBinder for communicating with the network stack
      * @hide
      * @see NetworkStackClient
      */
+    @SystemApi
+    @TestApi
     public static final String NETWORK_STACK_SERVICE = "network_stack";
 
     /**
@@ -4288,6 +4293,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/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 32803ab..87acbc1 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -7803,7 +7803,7 @@
             ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
         }
         ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
-        ai.resourceDirs = state.overlayPaths;
+        ai.resourceDirs = state.getAllOverlayPaths();
         ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes;
     }
 
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index f0f6753..da7623a 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -46,6 +46,8 @@
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -77,7 +79,9 @@
     public ArraySet<String> disabledComponents;
     public ArraySet<String> enabledComponents;
 
-    public String[] overlayPaths;
+    private String[] overlayPaths;
+    private ArrayMap<String, String[]> sharedLibraryOverlayPaths; // Lib name to overlay paths
+    private String[] cachedOverlayPaths;
 
     @UnsupportedAppUsage
     public PackageUserState() {
@@ -112,9 +116,33 @@
         enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
         overlayPaths =
             o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length);
+        if (o.sharedLibraryOverlayPaths != null) {
+            sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths);
+        }
         harmfulAppWarning = o.harmfulAppWarning;
     }
 
+    public String[] getOverlayPaths() {
+        return overlayPaths;
+    }
+
+    public void setOverlayPaths(String[] paths) {
+        overlayPaths = paths;
+        cachedOverlayPaths = null;
+    }
+
+    public Map<String, String[]> getSharedLibraryOverlayPaths() {
+        return sharedLibraryOverlayPaths;
+    }
+
+    public void setSharedLibraryOverlayPaths(String library, String[] paths) {
+        if (sharedLibraryOverlayPaths == null) {
+            sharedLibraryOverlayPaths = new ArrayMap<>();
+        }
+        sharedLibraryOverlayPaths.put(library, paths);
+        cachedOverlayPaths = null;
+    }
+
     /**
      * Test if this package is installed.
      */
@@ -235,6 +263,38 @@
         return isComponentEnabled;
     }
 
+    public String[] getAllOverlayPaths() {
+        if (overlayPaths == null && sharedLibraryOverlayPaths == null) {
+            return null;
+        }
+
+        if (cachedOverlayPaths != null) {
+            return cachedOverlayPaths;
+        }
+
+        final LinkedHashSet<String> paths = new LinkedHashSet<>();
+        if (overlayPaths != null) {
+            final int N = overlayPaths.length;
+            for (int i = 0; i < N; i++) {
+                paths.add(overlayPaths[i]);
+            }
+        }
+
+        if (sharedLibraryOverlayPaths != null) {
+            for (String[] libOverlayPaths : sharedLibraryOverlayPaths.values()) {
+                if (libOverlayPaths != null) {
+                    final int N = libOverlayPaths.length;
+                    for (int i = 0; i < N; i++) {
+                        paths.add(libOverlayPaths[i]);
+                    }
+                }
+            }
+        }
+
+        cachedOverlayPaths = paths.toArray(new String[0]);
+        return cachedOverlayPaths;
+    }
+
     @Override
     final public boolean equals(Object obj) {
         if (!(obj instanceof PackageUserState)) {
diff --git a/core/java/android/content/pm/parsing/PackageInfoUtils.java b/core/java/android/content/pm/parsing/PackageInfoUtils.java
index f2cf9a4..73a8d2a 100644
--- a/core/java/android/content/pm/parsing/PackageInfoUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoUtils.java
@@ -41,9 +41,11 @@
 import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
 import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
 import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
+import android.util.ArraySet;
 
 import com.android.internal.util.ArrayUtils;
 
+import java.util.LinkedHashSet;
 import java.util.Set;
 
 /** @hide */
@@ -545,7 +547,7 @@
             ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
         }
         ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
-        ai.resourceDirs = state.overlayPaths;
+        ai.resourceDirs = state.getAllOverlayPaths();
         ai.icon = (PackageParser.sUseRoundIcon && ai.roundIconRes != 0)
                 ? ai.roundIconRes : ai.iconRes;
     }
diff --git a/core/java/android/content/res/TEST_MAPPING b/core/java/android/content/res/TEST_MAPPING
new file mode 100644
index 0000000..daf9a14
--- /dev/null
+++ b/core/java/android/content/res/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksResourceLoaderTests"
+    }
+  ]
+}
diff --git a/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java b/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java
index 07d2443..6ead64c 100644
--- a/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java
@@ -25,9 +25,12 @@
  * A constrained high speed capture session for a {@link CameraDevice}, used for capturing high
  * speed images from the {@link CameraDevice} for high speed video recording use case.
  * <p>
- * A CameraHighSpeedCaptureSession is created by providing a set of target output surfaces to
- * {@link CameraDevice#createConstrainedHighSpeedCaptureSession}, Once created, the session is
- * active until a new session is created by the camera device, or the camera device is closed.
+ * A CameraConstrainedHighSpeedCaptureSession is created by providing a session configuration to
+ * {@link CameraDevice#createCaptureSession(SessionConfiguration)} with a type of
+ * {@link android.hardware.camera2.params.SessionConfiguration#SESSION_HIGH_SPEED}. The
+ * CameraCaptureSession returned from {@link CameraCaptureSession.StateCallback} can then be cast to
+ * a CameraConstrainedHighSpeedCaptureSession. Once created, the session is active until a new
+ * session is created by the camera device, or the camera device is closed.
  * </p>
  * <p>
  * An active high speed capture session is a specialized capture session that is only targeted at
@@ -37,8 +40,8 @@
  * accepts request lists created via {@link #createHighSpeedRequestList}, and the request list can
  * only be submitted to this session via {@link CameraCaptureSession#captureBurst captureBurst}, or
  * {@link CameraCaptureSession#setRepeatingBurst setRepeatingBurst}. See
- * {@link CameraDevice#createConstrainedHighSpeedCaptureSession} for more details of the
- * limitations.
+ * {@link CameraDevice#createCaptureSession(android.hardware.camera2.params.SessionConfiguration)}
+ * for more details of the limitations.
  * </p>
  * <p>
  * Creating a session is an expensive operation and can take several hundred milliseconds, since it
@@ -50,13 +53,6 @@
  * completed, then the {@link CameraCaptureSession.StateCallback#onConfigureFailed} is called, and
  * the session will not become active.
  * </p>
- * <!--
- * <p>
- * Any capture requests (repeating or non-repeating) submitted before the session is ready will be
- * queued up and will begin capture once the session becomes ready. In case the session cannot be
- * configured and {@link CameraCaptureSession.StateCallback#onConfigureFailed onConfigureFailed} is
- * called, all queued capture requests are discarded.  </p>
- * -->
  * <p>
  * If a new session is created by the camera device, then the previous session is closed, and its
  * associated {@link CameraCaptureSession.StateCallback#onClosed onClosed} callback will be
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index fb1ece2..cc06681 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -16,24 +16,22 @@
 
 package android.hardware.camera2;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import static android.hardware.camera2.ICameraDeviceUser.NORMAL_MODE;
-import static android.hardware.camera2.ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE;
 import android.hardware.camera2.params.InputConfiguration;
-import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.params.StreamConfigurationMap;
 import android.os.Handler;
 import android.view.Surface;
 
-import java.util.List;
-import java.util.Set;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Set;
 
 /**
  * <p>The CameraDevice class is a representation of a single camera connected to an
@@ -220,6 +218,224 @@
      * <p>Create a new camera capture session by providing the target output set of Surfaces to the
      * camera device.</p>
      *
+     * @param outputs The new set of Surfaces that should be made available as
+     *                targets for captured image data.
+     * @param callback The callback to notify about the status of the new capture session.
+     * @param handler The handler on which the callback should be invoked, or {@code null} to use
+     *                the current thread's {@link android.os.Looper looper}.
+     *
+     * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
+     *                                  the callback is null, or the handler is null but the current
+     *                                  thread has no looper.
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if the camera device has been closed
+     *
+     * @see CameraCaptureSession
+     * @see StreamConfigurationMap#getOutputFormats()
+     * @see StreamConfigurationMap#getOutputSizes(int)
+     * @see StreamConfigurationMap#getOutputSizes(Class)
+     * @deprecated Please use @{link
+     *      #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
+     *      full set of configuration options available.
+     */
+    @Deprecated
+    public abstract void createCaptureSession(@NonNull List<Surface> outputs,
+            @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
+            throws CameraAccessException;
+
+    /**
+     * <p>Create a new camera capture session by providing the target output set of Surfaces and
+     * its corresponding surface configuration to the camera device.</p>
+     *
+     * @see #createCaptureSession
+     * @see OutputConfiguration
+     * @deprecated Please use @{link
+     *      #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
+     *      full set of configuration options available.
+     */
+    @Deprecated
+    public abstract void createCaptureSessionByOutputConfigurations(
+            List<OutputConfiguration> outputConfigurations,
+            CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
+            throws CameraAccessException;
+    /**
+     * Create a new reprocessable camera capture session by providing the desired reprocessing
+     * input Surface configuration and the target output set of Surfaces to the camera device.
+     *
+     * @param inputConfig The configuration for the input {@link Surface}
+     * @param outputs The new set of Surfaces that should be made available as
+     *                targets for captured image data.
+     * @param callback The callback to notify about the status of the new capture session.
+     * @param handler The handler on which the callback should be invoked, or {@code null} to use
+     *                the current thread's {@link android.os.Looper looper}.
+     *
+     * @throws IllegalArgumentException if the input configuration is null or not supported, the set
+     *                                  of output Surfaces do not meet the requirements, the
+     *                                  callback is null, or the handler is null but the current
+     *                                  thread has no looper.
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if the camera device has been closed
+     *
+     * @see #createCaptureSession
+     * @see CameraCaptureSession
+     * @see StreamConfigurationMap#getInputFormats
+     * @see StreamConfigurationMap#getInputSizes
+     * @see StreamConfigurationMap#getValidOutputFormatsForInput
+     * @see StreamConfigurationMap#getOutputSizes
+     * @see android.media.ImageWriter
+     * @see android.media.ImageReader
+     * @deprecated Please use @{link
+     *      #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
+     *      full set of configuration options available.
+     */
+    @Deprecated
+    public abstract void createReprocessableCaptureSession(@NonNull InputConfiguration inputConfig,
+            @NonNull List<Surface> outputs, @NonNull CameraCaptureSession.StateCallback callback,
+            @Nullable Handler handler)
+            throws CameraAccessException;
+
+    /**
+     * Create a new reprocessable camera capture session by providing the desired reprocessing
+     * input configuration and output {@link OutputConfiguration}
+     * to the camera device.
+     *
+     * @see #createReprocessableCaptureSession
+     * @see OutputConfiguration
+     * @deprecated Please use @{link
+     *      #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
+     *      full set of configuration options available.
+     */
+    @Deprecated
+    public abstract void createReprocessableCaptureSessionByConfigurations(
+            @NonNull InputConfiguration inputConfig,
+            @NonNull List<OutputConfiguration> outputs,
+            @NonNull CameraCaptureSession.StateCallback callback,
+            @Nullable Handler handler)
+            throws CameraAccessException;
+
+    /**
+     * <p>Create a new constrained high speed capture session.</p>
+     *
+     * @param outputs The new set of Surfaces that should be made available as
+     *                targets for captured high speed image data.
+     * @param callback The callback to notify about the status of the new capture session.
+     * @param handler The handler on which the callback should be invoked, or {@code null} to use
+     *                the current thread's {@link android.os.Looper looper}.
+     *
+     * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
+     *                                  the callback is null, or the handler is null but the current
+     *                                  thread has no looper, or the camera device doesn't support
+     *                                  high speed video capability.
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if the camera device has been closed
+     *
+     * @see #createCaptureSession
+     * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
+     * @see StreamConfigurationMap#getHighSpeedVideoSizes
+     * @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
+     * @see CameraCaptureSession#captureBurst
+     * @see CameraCaptureSession#setRepeatingBurst
+     * @see CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList
+     * @deprecated Please use @{link
+     *      #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
+     *      full set of configuration options available.
+     */
+    @Deprecated
+    public abstract void createConstrainedHighSpeedCaptureSession(@NonNull List<Surface> outputs,
+            @NonNull CameraCaptureSession.StateCallback callback,
+            @Nullable Handler handler)
+            throws CameraAccessException;
+
+    /**
+     * Standard camera operation mode.
+     *
+     * @see #createCustomCaptureSession
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int SESSION_OPERATION_MODE_NORMAL =
+            0; // ICameraDeviceUser.NORMAL_MODE;
+
+    /**
+     * Constrained high-speed operation mode.
+     *
+     * @see #createCustomCaptureSession
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED =
+            1; // ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE;
+
+    /**
+     * First vendor-specific operating mode
+     *
+     * @see #createCustomCaptureSession
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int SESSION_OPERATION_MODE_VENDOR_START =
+            0x8000; // ICameraDeviceUser.VENDOR_MODE_START;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"SESSION_OPERATION_MODE"}, value =
+            {SESSION_OPERATION_MODE_NORMAL,
+             SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED,
+             SESSION_OPERATION_MODE_VENDOR_START})
+    public @interface SessionOperatingMode {};
+
+    /**
+     * Create a new camera capture session with a custom operating mode.
+     *
+     * @param inputConfig The configuration for the input {@link Surface} if a reprocessing session
+     *                is desired, or {@code null} otherwise.
+     * @param outputs The new set of {@link OutputConfiguration OutputConfigurations} that should be
+     *                made available as targets for captured image data.
+     * @param operatingMode The custom operating mode to use; a nonnegative value, either a custom
+     *                vendor value or one of the SESSION_OPERATION_MODE_* values.
+     * @param callback The callback to notify about the status of the new capture session.
+     * @param handler The handler on which the callback should be invoked, or {@code null} to use
+     *                the current thread's {@link android.os.Looper looper}.
+     *
+     * @throws IllegalArgumentException if the input configuration is null or not supported, the set
+     *                                  of output Surfaces do not meet the requirements, the
+     *                                  callback is null, or the handler is null but the current
+     *                                  thread has no looper.
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if the camera device has been closed
+     *
+     * @see #createCaptureSession
+     * @see #createReprocessableCaptureSession
+     * @see CameraCaptureSession
+     * @see OutputConfiguration
+     * @deprecated Please use @{link
+     *      #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
+     *      full set of configuration options available.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @Deprecated
+    public abstract void createCustomCaptureSession(
+            InputConfiguration inputConfig,
+            @NonNull List<OutputConfiguration> outputs,
+            @SessionOperatingMode int operatingMode,
+            @NonNull CameraCaptureSession.StateCallback callback,
+            @Nullable Handler handler)
+            throws CameraAccessException;
+
+    /**
+     * <p>Create a new {@link CameraCaptureSession} using a {@link SessionConfiguration} helper
+     * object that aggregates all supported parameters.</p>
      * <p>The active capture session determines the set of potential output Surfaces for
      * the camera device for each capture request. A given request may use all
      * or only some of the outputs. Once the CameraCaptureSession is created, requests can be
@@ -308,11 +524,15 @@
      * <p>Configuring a session with an empty or null list will close the current session, if
      * any. This can be used to release the current session's target surfaces for another use.</p>
      *
+     * <h3>Regular capture</h3>
+     *
      * <p>While any of the sizes from {@link StreamConfigurationMap#getOutputSizes} can be used when
      * a single output stream is configured, a given camera device may not be able to support all
      * combination of sizes, formats, and targets when multiple outputs are configured at once.  The
      * tables below list the maximum guaranteed resolutions for combinations of streams and targets,
-     * given the capabilities of the camera device.</p>
+     * given the capabilities of the camera device. These are valid for when the
+     * {@link android.hardware.camera2.params.SessionConfiguration#setInputConfiguration
+     * input configuration} is not set and therefore no reprocessing is active.</p>
      *
      * <p>If an application tries to create a session using a set of targets that exceed the limits
      * described in the below tables, one of three possibilities may occur. First, the session may
@@ -488,56 +708,22 @@
      * (either width or height) might not be supported, and capture session creation will fail if it
      * is not.</p>
      *
-     * @param outputs The new set of Surfaces that should be made available as
-     *                targets for captured image data.
-     * @param callback The callback to notify about the status of the new capture session.
-     * @param handler The handler on which the callback should be invoked, or {@code null} to use
-     *                the current thread's {@link android.os.Looper looper}.
-     *
-     * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
-     *                                  the callback is null, or the handler is null but the current
-     *                                  thread has no looper.
-     * @throws CameraAccessException if the camera device is no longer connected or has
-     *                               encountered a fatal error
-     * @throws IllegalStateException if the camera device has been closed
-     *
-     * @see CameraCaptureSession
-     * @see StreamConfigurationMap#getOutputFormats()
-     * @see StreamConfigurationMap#getOutputSizes(int)
-     * @see StreamConfigurationMap#getOutputSizes(Class)
-     */
-    public abstract void createCaptureSession(@NonNull List<Surface> outputs,
-            @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
-            throws CameraAccessException;
-
-    /**
-     * <p>Create a new camera capture session by providing the target output set of Surfaces and
-     * its corresponding surface configuration to the camera device.</p>
-     *
-     * @see #createCaptureSession
-     * @see OutputConfiguration
-     */
-    public abstract void createCaptureSessionByOutputConfigurations(
-            List<OutputConfiguration> outputConfigurations,
-            CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
-            throws CameraAccessException;
-    /**
-     * Create a new reprocessable camera capture session by providing the desired reprocessing
-     * input Surface configuration and the target output set of Surfaces to the camera device.
+     * <h3>Reprocessing</h3>
      *
      * <p>If a camera device supports YUV reprocessing
      * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING}) or PRIVATE
      * reprocessing
-     * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING}), besides
-     * the capture session created via {@link #createCaptureSession createCaptureSession}, the
+     * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING}), the
      * application can also create a reprocessable capture session to submit reprocess capture
-     * requests in addition to regular capture requests. A reprocess capture request takes the next
-     * available buffer from the session's input Surface, and sends it through the camera device's
-     * processing pipeline again, to produce buffers for the request's target output Surfaces. No
-     * new image data is captured for a reprocess request. However the input buffer provided by
-     * the application must be captured previously by the same camera device in the same session
-     * directly (e.g. for Zero-Shutter-Lag use case) or indirectly (e.g. combining multiple output
-     * images).</p>
+     * requests in addition to regular capture requests, by setting an
+     * {@link android.hardware.camera2.params.SessionConfiguration#setInputConfiguration
+     * input configuration} for the session. A reprocess capture request takes the next available
+     * buffer from the
+     * session's input Surface, and sends it through the camera device's processing pipeline again,
+     * to produce buffers for the request's target output Surfaces. No new image data is captured
+     * for a reprocess request. However the input buffer provided by the application must be
+     * captured previously by the same camera device in the same session directly (e.g. for
+     * Zero-Shutter-Lag use case) or indirectly (e.g. combining multiple output images).</p>
      *
      * <p>The active reprocessable capture session determines an input {@link Surface} and the set
      * of potential output Surfaces for the camera devices for each capture request. The application
@@ -570,10 +756,7 @@
      * <p>Starting from API level 30, recreating a reprocessable capture session will flush all the
      * queued but not yet processed buffers from the input surface.</p>
      *
-     * <p>The guaranteed stream configurations listed in
-     * {@link #createCaptureSession createCaptureSession} are also guaranteed to work for
-     * {@link #createReprocessableCaptureSession createReprocessableCaptureSession}. In addition,
-     * the configurations in the tables below are also guaranteed for creating a reprocessable
+     * <p>The configurations in the tables below are guaranteed for creating a reprocessable
      * capture session if the camera device supports YUV reprocessing or PRIVATE reprocessing.
      * However, not all output targets used to create a reprocessable session may be used in a
      * {@link CaptureRequest} simultaneously. For devices that support only 1 output target in a
@@ -602,7 +785,7 @@
      * <p>LIMITED-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
      * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}) devices
      * support at least the following stream combinations for creating a reprocessable capture
-     * session in addition to those listed in {@link #createCaptureSession createCaptureSession} for
+     * session in addition to those listed earlier for regular captures for
      * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices:
      *
      * <table>
@@ -671,74 +854,30 @@
      * </table><br>
      * </p>
      *
-     * <p>Clients can access the above mandatory stream combination tables via
-     * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p>
+     * <h3>Constrained high-speed recording</h3>
      *
-     * @param inputConfig The configuration for the input {@link Surface}
-     * @param outputs The new set of Surfaces that should be made available as
-     *                targets for captured image data.
-     * @param callback The callback to notify about the status of the new capture session.
-     * @param handler The handler on which the callback should be invoked, or {@code null} to use
-     *                the current thread's {@link android.os.Looper looper}.
-     *
-     * @throws IllegalArgumentException if the input configuration is null or not supported, the set
-     *                                  of output Surfaces do not meet the requirements, the
-     *                                  callback is null, or the handler is null but the current
-     *                                  thread has no looper.
-     * @throws CameraAccessException if the camera device is no longer connected or has
-     *                               encountered a fatal error
-     * @throws IllegalStateException if the camera device has been closed
-     *
-     * @see #createCaptureSession
-     * @see CameraCaptureSession
-     * @see StreamConfigurationMap#getInputFormats
-     * @see StreamConfigurationMap#getInputSizes
-     * @see StreamConfigurationMap#getValidOutputFormatsForInput
-     * @see StreamConfigurationMap#getOutputSizes
-     * @see android.media.ImageWriter
-     * @see android.media.ImageReader
-     */
-    public abstract void createReprocessableCaptureSession(@NonNull InputConfiguration inputConfig,
-            @NonNull List<Surface> outputs, @NonNull CameraCaptureSession.StateCallback callback,
-            @Nullable Handler handler)
-            throws CameraAccessException;
-
-    /**
-     * Create a new reprocessable camera capture session by providing the desired reprocessing
-     * input configuration and output {@link OutputConfiguration}
-     * to the camera device.
-     *
-     * @see #createReprocessableCaptureSession
-     * @see OutputConfiguration
-     *
-     */
-    public abstract void createReprocessableCaptureSessionByConfigurations(
-            @NonNull InputConfiguration inputConfig,
-            @NonNull List<OutputConfiguration> outputs,
-            @NonNull CameraCaptureSession.StateCallback callback,
-            @Nullable Handler handler)
-            throws CameraAccessException;
-
-    /**
-     * <p>Create a new constrained high speed capture session.</p>
-     *
-     * <p>The application can use normal capture session (created via {@link #createCaptureSession})
+     * <p>The application can use a
+     * {@link android.hardware.camera2.params.SessionConfiguration#SESSION_REGULAR
+     * normal capture session}
      * for high speed capture if the desired high speed FPS ranges are advertised by
      * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES}, in which case all API
      * semantics associated with normal capture sessions applies.</p>
      *
-     * <p>The method creates a specialized capture session that is only targeted at high speed
-     * video recording (>=120fps) use case if the camera device supports high speed video
-     * capability (i.e., {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} contains
-     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO}).
-     * Therefore, it has special characteristics compared with a normal capture session:</p>
+     * <p>A
+     * {@link android.hardware.camera2.params.SessionConfiguration#SESSION_HIGH_SPEED
+     * high-speed capture session}
+     * can be use for high speed video recording (>=120fps) when the camera device supports high
+     * speed video capability (i.e., {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
+     * contains {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO}).
+     * A constrained high-speed capture session has special limitations compared with a normal
+     * capture session:</p>
      *
      * <ul>
      *
-     * <li>In addition to the output target Surface requirements specified by the
-     *   {@link #createCaptureSession} method, an active high speed capture session will support up
-     *   to 2 output Surfaces, though the application might choose to configure just one Surface
-     *   (e.g., preview only). All Surfaces must be either video encoder surfaces (acquired by
+     * <li>In addition to the output target Surface requirements specified above for regular
+     *   captures, a high speed capture session will only support up to 2 output Surfaces, though
+     *   the application might choose to configure just one Surface (e.g., preview only). All
+     *   Surfaces must be either video encoder surfaces (acquired by
      *   {@link android.media.MediaRecorder#getSurface} or
      *   {@link android.media.MediaCodec#createInputSurface}) or preview surfaces (obtained from
      *   {@link android.view.SurfaceView}, {@link android.graphics.SurfaceTexture} via
@@ -774,116 +913,6 @@
      *
      * </ul>
      *
-     * @param outputs The new set of Surfaces that should be made available as
-     *                targets for captured high speed image data.
-     * @param callback The callback to notify about the status of the new capture session.
-     * @param handler The handler on which the callback should be invoked, or {@code null} to use
-     *                the current thread's {@link android.os.Looper looper}.
-     *
-     * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
-     *                                  the callback is null, or the handler is null but the current
-     *                                  thread has no looper, or the camera device doesn't support
-     *                                  high speed video capability.
-     * @throws CameraAccessException if the camera device is no longer connected or has
-     *                               encountered a fatal error
-     * @throws IllegalStateException if the camera device has been closed
-     *
-     * @see #createCaptureSession
-     * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
-     * @see StreamConfigurationMap#getHighSpeedVideoSizes
-     * @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor
-     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
-     * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
-     * @see CameraCaptureSession#captureBurst
-     * @see CameraCaptureSession#setRepeatingBurst
-     * @see CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList
-     */
-    public abstract void createConstrainedHighSpeedCaptureSession(@NonNull List<Surface> outputs,
-            @NonNull CameraCaptureSession.StateCallback callback,
-            @Nullable Handler handler)
-            throws CameraAccessException;
-
-    /**
-     * Standard camera operation mode.
-     *
-     * @see #createCustomCaptureSession
-     * @hide
-     */
-    @SystemApi
-    @TestApi
-    public static final int SESSION_OPERATION_MODE_NORMAL =
-            0; // ICameraDeviceUser.NORMAL_MODE;
-
-    /**
-     * Constrained high-speed operation mode.
-     *
-     * @see #createCustomCaptureSession
-     * @hide
-     */
-    @SystemApi
-    @TestApi
-    public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED =
-            1; // ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE;
-
-    /**
-     * First vendor-specific operating mode
-     *
-     * @see #createCustomCaptureSession
-     * @hide
-     */
-    @SystemApi
-    @TestApi
-    public static final int SESSION_OPERATION_MODE_VENDOR_START =
-            0x8000; // ICameraDeviceUser.VENDOR_MODE_START;
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"SESSION_OPERATION_MODE"}, value =
-            {SESSION_OPERATION_MODE_NORMAL,
-             SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED,
-             SESSION_OPERATION_MODE_VENDOR_START})
-    public @interface SessionOperatingMode {};
-
-    /**
-     * Create a new camera capture session with a custom operating mode.
-     *
-     * @param inputConfig The configuration for the input {@link Surface} if a reprocessing session
-     *                is desired, or {@code null} otherwise.
-     * @param outputs The new set of {@link OutputConfiguration OutputConfigurations} that should be
-     *                made available as targets for captured image data.
-     * @param operatingMode The custom operating mode to use; a nonnegative value, either a custom
-     *                vendor value or one of the SESSION_OPERATION_MODE_* values.
-     * @param callback The callback to notify about the status of the new capture session.
-     * @param handler The handler on which the callback should be invoked, or {@code null} to use
-     *                the current thread's {@link android.os.Looper looper}.
-     *
-     * @throws IllegalArgumentException if the input configuration is null or not supported, the set
-     *                                  of output Surfaces do not meet the requirements, the
-     *                                  callback is null, or the handler is null but the current
-     *                                  thread has no looper.
-     * @throws CameraAccessException if the camera device is no longer connected or has
-     *                               encountered a fatal error
-     * @throws IllegalStateException if the camera device has been closed
-     *
-     * @see #createCaptureSession
-     * @see #createReprocessableCaptureSession
-     * @see CameraCaptureSession
-     * @see OutputConfiguration
-     * @hide
-     */
-    @SystemApi
-    @TestApi
-    public abstract void createCustomCaptureSession(
-            InputConfiguration inputConfig,
-            @NonNull List<OutputConfiguration> outputs,
-            @SessionOperatingMode int operatingMode,
-            @NonNull CameraCaptureSession.StateCallback callback,
-            @Nullable Handler handler)
-            throws CameraAccessException;
-
-    /**
-     * <p>Create a new {@link CameraCaptureSession} using a {@link SessionConfiguration} helper
-     * object that aggregates all supported parameters.</p>
      *
      * @param config A session configuration (see {@link SessionConfiguration}).
      *
@@ -997,7 +1026,7 @@
      *
      * @see CaptureRequest.Builder
      * @see TotalCaptureResult
-     * @see CameraDevice#createReprocessableCaptureSession
+     * @see CameraDevice#createCaptureSession(android.hardware.camera2.params.SessionConfiguration)
      * @see android.media.ImageWriter
      */
     @NonNull
@@ -1028,7 +1057,8 @@
      * <p>This method performs a runtime check of a given {@link SessionConfiguration}. The result
      * confirms whether or not the passed session configuration can be successfully used to
      * create a camera capture session using
-     * {@link CameraDevice#createCaptureSession(SessionConfiguration)}.
+     * {@link CameraDevice#createCaptureSession(
+     * android.hardware.camera2.params.SessionConfiguration)}.
      * </p>
      *
      * <p>The method can be called at any point before, during and after active capture session.
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 8e0a46d..ec13a36 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1121,12 +1121,16 @@
     //
 
     /**
-     * <p>Timestamps from {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} are in nanoseconds and monotonic,
-     * but can not be compared to timestamps from other subsystems
-     * (e.g. accelerometer, gyro etc.), or other instances of the same or different
-     * camera devices in the same system. Timestamps between streams and results for
-     * a single camera instance are comparable, and the timestamps for all buffers
-     * and the result metadata generated by a single capture are identical.</p>
+     * <p>Timestamps from {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} are in nanoseconds and monotonic, but can
+     * not be compared to timestamps from other subsystems (e.g. accelerometer, gyro etc.),
+     * or other instances of the same or different camera devices in the same system with
+     * accuracy. However, the timestamps are roughly in the same timebase as
+     * {@link android.os.SystemClock#uptimeMillis }.  The accuracy is sufficient for tasks
+     * like A/V synchronization for video recording, at least, and the timestamps can be
+     * directly used together with timestamps from the audio subsystem for that task.</p>
+     * <p>Timestamps between streams and results for a single camera instance are comparable,
+     * and the timestamps for all buffers and the result metadata generated by a single
+     * capture are identical.</p>
      *
      * @see CaptureResult#SENSOR_TIMESTAMP
      * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
@@ -1137,6 +1141,14 @@
      * <p>Timestamps from {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} are in the same timebase as
      * {@link android.os.SystemClock#elapsedRealtimeNanos },
      * and they can be compared to other timestamps using that base.</p>
+     * <p>When buffers from a REALTIME device are passed directly to a video encoder from the
+     * camera, automatic compensation is done to account for differing timebases of the
+     * audio and camera subsystems.  If the application is receiving buffers and then later
+     * sending them to a video encoder or other application where they are compared with
+     * audio subsystem timestamps or similar, this compensation is not present.  In those
+     * cases, applications need to adjust the timestamps themselves.  Since {@link android.os.SystemClock#elapsedRealtimeNanos } and {@link android.os.SystemClock#uptimeMillis } only diverge while the device is asleep, an
+     * offset between the two sources can be measured once per active session and applied
+     * to timestamps for sufficient accuracy for A/V sync.</p>
      *
      * @see CaptureResult#SENSOR_TIMESTAMP
      * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 555ff9a..47a897c 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -17,10 +17,11 @@
 
 package android.hardware.camera2.params;
 
+import static com.android.internal.util.Preconditions.*;
+
 import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
@@ -32,14 +33,12 @@
 import android.os.Parcelable;
 import android.util.Log;
 
-import java.util.Collections;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.concurrent.Executor;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-
-import static com.android.internal.util.Preconditions.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * A helper class that aggregates all supported arguments for capture session initialization.
@@ -61,6 +60,12 @@
      * A high speed session type that can only contain instances of {@link OutputConfiguration}.
      * The outputs can run using high speed FPS ranges. Calls to {@link #setInputConfiguration}
      * are not supported.
+     * <p>
+     * When using this type, the CameraCaptureSession returned by
+     * {@link android.hardware.camera2.CameraCaptureSession.StateCallback} can be cast to a
+     * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession} to access the extra
+     * methods for constrained high speed recording.
+     * </p>
      *
      * @see CameraDevice#createConstrainedHighSpeedCaptureSession
      */
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index 43f3787..d43a619 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -20,6 +20,7 @@
 import android.hardware.soundtrigger.ModelParams;
 import android.media.AudioFormat;
 import android.media.audio.common.AudioConfig;
+import android.media.soundtrigger_middleware.AudioCapabilities;
 import android.media.soundtrigger_middleware.ConfidenceLevel;
 import android.media.soundtrigger_middleware.ModelParameterRange;
 import android.media.soundtrigger_middleware.Phrase;
@@ -56,7 +57,8 @@
                 properties.maxBufferMs,
                 properties.concurrentCapture,
                 properties.powerConsumptionMw,
-                properties.triggerInEvent
+                properties.triggerInEvent,
+                aidl2apiAudioCapabilities(properties.audioCapabilities)
         );
     }
 
@@ -145,6 +147,7 @@
                     apiConfig.keyphrases[i]);
         }
         aidlConfig.data = Arrays.copyOf(apiConfig.data, apiConfig.data.length);
+        aidlConfig.audioCapabilities = api2aidlAudioCapabilities(apiConfig.audioCapabilities);
         return aidlConfig;
     }
 
@@ -326,4 +329,26 @@
         }
         return new SoundTrigger.ModelParamRange(aidlRange.minInclusive, aidlRange.maxInclusive);
     }
+
+    public static int aidl2apiAudioCapabilities(int aidlCapabilities) {
+        int result = 0;
+        if ((aidlCapabilities & AudioCapabilities.ECHO_CANCELLATION) != 0) {
+            result |= SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION;
+        }
+        if ((aidlCapabilities & AudioCapabilities.NOISE_SUPPRESSION) != 0) {
+            result |= SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION;
+        }
+        return result;
+    }
+
+    public static int api2aidlAudioCapabilities(int apiCapabilities) {
+        int result = 0;
+        if ((apiCapabilities & SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION) != 0) {
+            result |= AudioCapabilities.ECHO_CANCELLATION;
+        }
+        if ((apiCapabilities & SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION) != 0) {
+            result |= AudioCapabilities.NOISE_SUPPRESSION;
+        }
+        return result;
+    }
 }
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index d872009..a5e0f04 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -24,6 +24,7 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -42,6 +43,8 @@
 import android.os.ServiceManager;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.UUID;
@@ -83,6 +86,30 @@
      *
      ****************************************************************************/
     public static final class ModuleProperties implements Parcelable {
+
+        /**
+         * Bit field values of AudioCapabilities supported by the implemented HAL
+         * driver.
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag = true, prefix = { "AUDIO_CAPABILITY_" }, value = {
+                CAPABILITY_ECHO_CANCELLATION,
+                CAPABILITY_NOISE_SUPPRESSION
+        })
+        public @interface AudioCapabilities {}
+
+        /**
+         * If set the underlying module supports AEC.
+         * Describes bit field {@link ModuleProperties#audioCapabilities}
+         */
+        public static final int CAPABILITY_ECHO_CANCELLATION = 0x1;
+        /**
+         * If set, the underlying module supports noise suppression.
+         * Describes bit field {@link ModuleProperties#audioCapabilities}
+         */
+        public static final int CAPABILITY_NOISE_SUPPRESSION = 0x2;
+
         /** Unique module ID provided by the native service */
         public final int id;
 
@@ -137,12 +164,19 @@
          * recognition callback event */
         public final boolean returnsTriggerInEvent;
 
+        /**
+         * Bit field encoding of the AudioCapabilities
+         * supported by the firmware.
+         */
+        @AudioCapabilities
+        public final int audioCapabilities;
+
         ModuleProperties(int id, @NonNull String implementor, @NonNull String description,
                 @NonNull String uuid, int version, @NonNull String supportedModelArch,
                 int maxSoundModels, int maxKeyphrases, int maxUsers, int recognitionModes,
                 boolean supportsCaptureTransition, int maxBufferMs,
                 boolean supportsConcurrentCapture, int powerConsumptionMw,
-                boolean returnsTriggerInEvent) {
+                boolean returnsTriggerInEvent, int audioCapabilities) {
             this.id = id;
             this.implementor = requireNonNull(implementor);
             this.description = requireNonNull(description);
@@ -158,6 +192,7 @@
             this.supportsConcurrentCapture = supportsConcurrentCapture;
             this.powerConsumptionMw = powerConsumptionMw;
             this.returnsTriggerInEvent = returnsTriggerInEvent;
+            this.audioCapabilities = audioCapabilities;
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<ModuleProperties> CREATOR
@@ -187,10 +222,11 @@
             boolean supportsConcurrentCapture = in.readByte() == 1;
             int powerConsumptionMw = in.readInt();
             boolean returnsTriggerInEvent = in.readByte() == 1;
+            int audioCapabilities = in.readInt();
             return new ModuleProperties(id, implementor, description, uuid, version,
                     supportedModelArch, maxSoundModels, maxKeyphrases, maxUsers, recognitionModes,
                     supportsCaptureTransition, maxBufferMs, supportsConcurrentCapture,
-                    powerConsumptionMw, returnsTriggerInEvent);
+                    powerConsumptionMw, returnsTriggerInEvent, audioCapabilities);
         }
 
         @Override
@@ -210,6 +246,7 @@
             dest.writeByte((byte) (supportsConcurrentCapture ? 1 : 0));
             dest.writeInt(powerConsumptionMw);
             dest.writeByte((byte) (returnsTriggerInEvent ? 1 : 0));
+            dest.writeInt(audioCapabilities);
         }
 
         @Override
@@ -227,7 +264,8 @@
                     + ", supportsCaptureTransition=" + supportsCaptureTransition + ", maxBufferMs="
                     + maxBufferMs + ", supportsConcurrentCapture=" + supportsConcurrentCapture
                     + ", powerConsumptionMw=" + powerConsumptionMw
-                    + ", returnsTriggerInEvent=" + returnsTriggerInEvent + "]";
+                    + ", returnsTriggerInEvent=" + returnsTriggerInEvent
+                    + ", audioCapabilities=" + audioCapabilities + "]";
         }
     }
 
@@ -265,16 +303,20 @@
         @NonNull
         public final UUID vendorUuid;
 
+        /** vendor specific version number of the model */
+        public final int version;
+
         /** Opaque data. For use by vendor implementation and enrollment application */
         @UnsupportedAppUsage
         @NonNull
         public final byte[] data;
 
         public SoundModel(@NonNull UUID uuid, @Nullable UUID vendorUuid, int type,
-                @Nullable byte[] data) {
+                @Nullable byte[] data, int version) {
             this.uuid = requireNonNull(uuid);
             this.vendorUuid = vendorUuid != null ? vendorUuid : new UUID(0, 0);
             this.type = type;
+            this.version = version;
             this.data = data != null ? data : new byte[0];
         }
 
@@ -282,6 +324,7 @@
         public int hashCode() {
             final int prime = 31;
             int result = 1;
+            result = prime * result + version;
             result = prime * result + Arrays.hashCode(data);
             result = prime * result + type;
             result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
@@ -312,6 +355,8 @@
                 return false;
             if (!Arrays.equals(data, other.data))
                 return false;
+            if (version != other.version)
+                return false;
             return true;
         }
     }
@@ -461,14 +506,19 @@
         @NonNull
         public final Keyphrase[] keyphrases; // keyword phrases in model
 
-        @UnsupportedAppUsage
         public KeyphraseSoundModel(
                 @NonNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data,
-                @Nullable Keyphrase[] keyphrases) {
-            super(uuid, vendorUuid, TYPE_KEYPHRASE, data);
+                @Nullable Keyphrase[] keyphrases, int version) {
+            super(uuid, vendorUuid, TYPE_KEYPHRASE, data, version);
             this.keyphrases = keyphrases != null ? keyphrases : new Keyphrase[0];
         }
 
+        @UnsupportedAppUsage
+        public KeyphraseSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
+                @Nullable byte[] data, @Nullable Keyphrase[] keyphrases) {
+            this(uuid, vendorUuid, data, keyphrases, -1);
+        }
+
         public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseSoundModel> CREATOR
                 = new Parcelable.Creator<KeyphraseSoundModel>() {
             public KeyphraseSoundModel createFromParcel(Parcel in) {
@@ -487,9 +537,10 @@
             if (length >= 0) {
                 vendorUuid = UUID.fromString(in.readString());
             }
+            int version = in.readInt();
             byte[] data = in.readBlob();
             Keyphrase[] keyphrases = in.createTypedArray(Keyphrase.CREATOR);
-            return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases);
+            return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases, version);
         }
 
         @Override
@@ -508,13 +559,16 @@
             }
             dest.writeBlob(data);
             dest.writeTypedArray(keyphrases, flags);
+            dest.writeInt(version);
         }
 
         @Override
         public String toString() {
             return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(keyphrases)
                     + ", uuid=" + uuid + ", vendorUuid=" + vendorUuid
-                    + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]";
+                    + ", type=" + type
+                    + ", data=" + (data == null ? 0 : data.length)
+                    + ", version=" + version + "]";
         }
 
         @Override
@@ -560,10 +614,15 @@
             }
         };
 
+        public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
+                @Nullable byte[] data, int version) {
+            super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data, version);
+        }
+
         @UnsupportedAppUsage
         public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
                 @Nullable byte[] data) {
-            super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data);
+            this(uuid, vendorUuid, data, -1);
         }
 
         @Override
@@ -579,7 +638,8 @@
                 vendorUuid = UUID.fromString(in.readString());
             }
             byte[] data = in.readBlob();
-            return new GenericSoundModel(uuid, vendorUuid, data);
+            int version = in.readInt();
+            return new GenericSoundModel(uuid, vendorUuid, data, version);
         }
 
         @Override
@@ -592,12 +652,15 @@
                 dest.writeString(vendorUuid.toString());
             }
             dest.writeBlob(data);
+            dest.writeInt(version);
         }
 
         @Override
         public String toString() {
             return "GenericSoundModel [uuid=" + uuid + ", vendorUuid=" + vendorUuid
-                    + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]";
+                    + ", type=" + type
+                    + ", data=" + (data == null ? 0 : data.length)
+                    + ", version=" + version + "]";
         }
     }
 
@@ -1049,13 +1112,27 @@
         @NonNull
         public final byte[] data;
 
-        @UnsupportedAppUsage
+        /**
+         * Bit field encoding of the AudioCapabilities
+         * supported by the firmware.
+         */
+        @ModuleProperties.AudioCapabilities
+        public final int audioCapabilities;
+
         public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
-                @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) {
+                @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data,
+                int audioCapabilities) {
             this.captureRequested = captureRequested;
             this.allowMultipleTriggers = allowMultipleTriggers;
             this.keyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0];
             this.data = data != null ? data : new byte[0];
+            this.audioCapabilities = audioCapabilities;
+        }
+
+        @UnsupportedAppUsage
+        public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
+                @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) {
+            this(captureRequested, allowMultipleTriggers, keyphrases, data, 0);
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<RecognitionConfig> CREATOR
@@ -1075,7 +1152,9 @@
             KeyphraseRecognitionExtra[] keyphrases =
                     in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
             byte[] data = in.readBlob();
-            return new RecognitionConfig(captureRequested, allowMultipleTriggers, keyphrases, data);
+            int audioCapabilities = in.readInt();
+            return new RecognitionConfig(captureRequested, allowMultipleTriggers, keyphrases, data,
+                    audioCapabilities);
         }
 
         @Override
@@ -1084,6 +1163,7 @@
             dest.writeByte((byte) (allowMultipleTriggers ? 1 : 0));
             dest.writeTypedArray(keyphrases, flags);
             dest.writeBlob(data);
+            dest.writeInt(audioCapabilities);
         }
 
         @Override
@@ -1095,7 +1175,8 @@
         public String toString() {
             return "RecognitionConfig [captureRequested=" + captureRequested
                     + ", allowMultipleTriggers=" + allowMultipleTriggers + ", keyphrases="
-                    + Arrays.toString(keyphrases) + ", data=" + Arrays.toString(data) + "]";
+                    + Arrays.toString(keyphrases) + ", data=" + Arrays.toString(data)
+                    + ", audioCapabilities=" + Integer.toHexString(audioCapabilities) + "]";
         }
     }
 
diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java
index a66fcae..fb35b4b 100644
--- a/core/java/android/net/CaptivePortal.java
+++ b/core/java/android/net/CaptivePortal.java
@@ -60,6 +60,18 @@
     @SystemApi
     @TestApi
     public static final int APP_RETURN_WANTED_AS_IS = 2;
+    /** Event offset of request codes from captive portal application. */
+    private static final int APP_REQUEST_BASE = 100;
+    /**
+     * Request code from the captive portal application, indicating that the network condition may
+     * have changed and the network should be re-validated.
+     * @see ICaptivePortal#appRequest(int)
+     * @see android.net.INetworkMonitor#forceReevaluation(int)
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int APP_REQUEST_REEVALUATION_REQUIRED = APP_REQUEST_BASE + 0;
 
     private final IBinder mBinder;
 
@@ -136,6 +148,19 @@
     }
 
     /**
+     * Request that the system reevaluates the captive portal status.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public void reevaluateNetwork() {
+        try {
+            ICaptivePortal.Stub.asInterface(mBinder).appRequest(APP_REQUEST_REEVALUATION_REQUIRED);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Log a captive portal login event.
      * @param eventId one of the CAPTIVE_PORTAL_LOGIN_* constants in metrics_constants.proto.
      * @param packageName captive portal application package name.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 03d4200..4bf3e90 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3121,8 +3121,6 @@
     @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");
         }
 
@@ -3175,9 +3173,8 @@
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
-            NetworkCapabilities nc, int score, NetworkMisc misc) {
-        return registerNetworkAgent(messenger, ni, lp, nc, score, misc,
-                NetworkFactory.SerialNumber.NONE);
+            NetworkCapabilities nc, int score, NetworkAgentConfig config) {
+        return registerNetworkAgent(messenger, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
     }
 
     /**
@@ -3187,10 +3184,9 @@
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
-            NetworkCapabilities nc, int score, NetworkMisc misc, int factorySerialNumber) {
+            NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
         try {
-            return mService.registerNetworkAgent(messenger, ni, lp, nc, score, misc,
-                    factorySerialNumber);
+            return mService.registerNetworkAgent(messenger, ni, lp, nc, score, config, providerId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/ICaptivePortal.aidl b/core/java/android/net/ICaptivePortal.aidl
index 707b4f6..fe21905 100644
--- a/core/java/android/net/ICaptivePortal.aidl
+++ b/core/java/android/net/ICaptivePortal.aidl
@@ -21,6 +21,7 @@
  * @hide
  */
 oneway interface ICaptivePortal {
+    void appRequest(int request);
     void appResponse(int response);
     void logEvent(int eventId, String packageName);
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index e6a0379..7691beb 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -20,9 +20,9 @@
 import android.net.ConnectionInfo;
 import android.net.LinkProperties;
 import android.net.Network;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
-import android.net.NetworkMisc;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkState;
@@ -153,7 +153,8 @@
     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);
+            in NetworkCapabilities nc, int score, in NetworkAgentConfig config,
+            in int factorySerialNumber);
 
     NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
             in Messenger messenger, int timeoutSec, in IBinder binder, int legacy);
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 9994f9f..5fa515a 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -23,6 +23,8 @@
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
+import android.net.netstats.provider.INetworkStatsProvider;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
 import android.os.IBinder;
 import android.os.Messenger;
 import com.android.internal.net.VpnInfo;
@@ -89,4 +91,7 @@
     /** Get the total network stats information since boot */
     long getTotalStats(int type);
 
+    /** Registers a network stats provider */
+    INetworkStatsProviderCallback registerNetworkStatsProvider(String tag,
+            in INetworkStatsProvider provider);
 }
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 60bd573..fc72eec 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -43,11 +43,12 @@
  *
  * @hide
  */
-public abstract class NetworkAgent extends Handler {
+public abstract class NetworkAgent {
     // Guaranteed to be valid (not NETID_UNSET), otherwise registerNetworkAgent() would have thrown
     // an exception.
     public final int netId;
 
+    private final Handler mHandler;
     private volatile AsyncChannel mAsyncChannel;
     private final String LOG_TAG;
     private static final boolean DBG = true;
@@ -58,7 +59,7 @@
     private static final long BW_REFRESH_MIN_WIN_MS = 500;
     private boolean mPollLceScheduled = false;
     private AtomicBoolean mPollLcePending = new AtomicBoolean(false);
-    public final int mFactorySerialNumber;
+    public final int mProviderId;
 
     private static final int BASE = Protocol.BASE_NETWORK_AGENT;
 
@@ -219,25 +220,25 @@
     // the entire tree.
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score) {
-        this(looper, context, logTag, ni, nc, lp, score, null, NetworkFactory.SerialNumber.NONE);
+        this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE);
     }
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
-            NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
-        this(looper, context, logTag, ni, nc, lp, score, misc, NetworkFactory.SerialNumber.NONE);
+            NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) {
+        this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE);
     }
 
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
-            NetworkCapabilities nc, LinkProperties lp, int score, int factorySerialNumber) {
-        this(looper, context, logTag, ni, nc, lp, score, null, factorySerialNumber);
+            NetworkCapabilities nc, LinkProperties lp, int score, int providerId) {
+        this(looper, context, logTag, ni, nc, lp, score, null, providerId);
     }
 
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
-            NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc,
-            int factorySerialNumber) {
-        super(looper);
+            NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
+            int providerId) {
+        mHandler = new NetworkAgentHandler(looper);
         LOG_TAG = logTag;
         mContext = context;
-        mFactorySerialNumber = factorySerialNumber;
+        mProviderId = providerId;
         if (ni == null || nc == null || lp == null) {
             throw new IllegalArgumentException();
         }
@@ -245,117 +246,124 @@
         if (VDBG) log("Registering NetworkAgent");
         ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
-        netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
-                new LinkProperties(lp), new NetworkCapabilities(nc), score, misc,
-                factorySerialNumber);
+        netId = cm.registerNetworkAgent(new Messenger(mHandler), new NetworkInfo(ni),
+                new LinkProperties(lp), new NetworkCapabilities(nc), score, config,
+                providerId);
     }
 
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
-                if (mAsyncChannel != null) {
-                    log("Received new connection while already connected!");
-                } else {
-                    if (VDBG) log("NetworkAgent fully connected");
-                    AsyncChannel ac = new AsyncChannel();
-                    ac.connected(null, this, msg.replyTo);
-                    ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
-                            AsyncChannel.STATUS_SUCCESSFUL);
-                    synchronized (mPreConnectedQueue) {
-                        mAsyncChannel = ac;
-                        for (Message m : mPreConnectedQueue) {
-                            ac.sendMessage(m);
-                        }
-                        mPreConnectedQueue.clear();
-                    }
-                }
-                break;
-            }
-            case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
-                if (VDBG) log("CMD_CHANNEL_DISCONNECT");
-                if (mAsyncChannel != null) mAsyncChannel.disconnect();
-                break;
-            }
-            case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
-                if (DBG) log("NetworkAgent channel lost");
-                // let the client know CS is done with us.
-                unwanted();
-                synchronized (mPreConnectedQueue) {
-                    mAsyncChannel = null;
-                }
-                break;
-            }
-            case CMD_SUSPECT_BAD: {
-                log("Unhandled Message " + msg);
-                break;
-            }
-            case CMD_REQUEST_BANDWIDTH_UPDATE: {
-                long currentTimeMs = System.currentTimeMillis();
-                if (VDBG) {
-                    log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
-                }
-                if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
-                    mPollLceScheduled = false;
-                    if (mPollLcePending.getAndSet(true) == false) {
-                        pollLceData();
-                    }
-                } else {
-                    // deliver the request at a later time rather than discard it completely.
-                    if (!mPollLceScheduled) {
-                        long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS -
-                                currentTimeMs + 1;
-                        mPollLceScheduled = sendEmptyMessageDelayed(
-                                CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
-                    }
-                }
-                break;
-            }
-            case CMD_REPORT_NETWORK_STATUS: {
-                String redirectUrl = ((Bundle)msg.obj).getString(REDIRECT_URL_KEY);
-                if (VDBG) {
-                    log("CMD_REPORT_NETWORK_STATUS(" +
-                            (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl);
-                }
-                networkStatus(msg.arg1, redirectUrl);
-                break;
-            }
-            case CMD_SAVE_ACCEPT_UNVALIDATED: {
-                saveAcceptUnvalidated(msg.arg1 != 0);
-                break;
-            }
-            case CMD_START_SOCKET_KEEPALIVE: {
-                startSocketKeepalive(msg);
-                break;
-            }
-            case CMD_STOP_SOCKET_KEEPALIVE: {
-                stopSocketKeepalive(msg);
-                break;
-            }
+    private class NetworkAgentHandler extends Handler {
+        NetworkAgentHandler(Looper looper) {
+            super(looper);
+        }
 
-            case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
-                ArrayList<Integer> thresholds =
-                        ((Bundle) msg.obj).getIntegerArrayList("thresholds");
-                // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
-                // rather than convert to int[].
-                int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
-                for (int i = 0; i < intThresholds.length; i++) {
-                    intThresholds[i] = thresholds.get(i);
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
+                    if (mAsyncChannel != null) {
+                        log("Received new connection while already connected!");
+                    } else {
+                        if (VDBG) log("NetworkAgent fully connected");
+                        AsyncChannel ac = new AsyncChannel();
+                        ac.connected(null, this, msg.replyTo);
+                        ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+                                AsyncChannel.STATUS_SUCCESSFUL);
+                        synchronized (mPreConnectedQueue) {
+                            mAsyncChannel = ac;
+                            for (Message m : mPreConnectedQueue) {
+                                ac.sendMessage(m);
+                            }
+                            mPreConnectedQueue.clear();
+                        }
+                    }
+                    break;
                 }
-                setSignalStrengthThresholds(intThresholds);
-                break;
-            }
-            case CMD_PREVENT_AUTOMATIC_RECONNECT: {
-                preventAutomaticReconnect();
-                break;
-            }
-            case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
-                addKeepalivePacketFilter(msg);
-                break;
-            }
-            case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
-                removeKeepalivePacketFilter(msg);
-                break;
+                case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
+                    if (VDBG) log("CMD_CHANNEL_DISCONNECT");
+                    if (mAsyncChannel != null) mAsyncChannel.disconnect();
+                    break;
+                }
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+                    if (DBG) log("NetworkAgent channel lost");
+                    // let the client know CS is done with us.
+                    unwanted();
+                    synchronized (mPreConnectedQueue) {
+                        mAsyncChannel = null;
+                    }
+                    break;
+                }
+                case CMD_SUSPECT_BAD: {
+                    log("Unhandled Message " + msg);
+                    break;
+                }
+                case CMD_REQUEST_BANDWIDTH_UPDATE: {
+                    long currentTimeMs = System.currentTimeMillis();
+                    if (VDBG) {
+                        log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
+                    }
+                    if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
+                        mPollLceScheduled = false;
+                        if (!mPollLcePending.getAndSet(true)) {
+                            pollLceData();
+                        }
+                    } else {
+                        // deliver the request at a later time rather than discard it completely.
+                        if (!mPollLceScheduled) {
+                            long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS
+                                    - currentTimeMs + 1;
+                            mPollLceScheduled = sendEmptyMessageDelayed(
+                                    CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
+                        }
+                    }
+                    break;
+                }
+                case CMD_REPORT_NETWORK_STATUS: {
+                    String redirectUrl = ((Bundle) msg.obj).getString(REDIRECT_URL_KEY);
+                    if (VDBG) {
+                        log("CMD_REPORT_NETWORK_STATUS("
+                                + (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ")
+                                + redirectUrl);
+                    }
+                    networkStatus(msg.arg1, redirectUrl);
+                    break;
+                }
+                case CMD_SAVE_ACCEPT_UNVALIDATED: {
+                    saveAcceptUnvalidated(msg.arg1 != 0);
+                    break;
+                }
+                case CMD_START_SOCKET_KEEPALIVE: {
+                    startSocketKeepalive(msg);
+                    break;
+                }
+                case CMD_STOP_SOCKET_KEEPALIVE: {
+                    stopSocketKeepalive(msg);
+                    break;
+                }
+
+                case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
+                    ArrayList<Integer> thresholds =
+                            ((Bundle) msg.obj).getIntegerArrayList("thresholds");
+                    // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
+                    // rather than convert to int[].
+                    int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
+                    for (int i = 0; i < intThresholds.length; i++) {
+                        intThresholds[i] = thresholds.get(i);
+                    }
+                    setSignalStrengthThresholds(intThresholds);
+                    break;
+                }
+                case CMD_PREVENT_AUTOMATIC_RECONNECT: {
+                    preventAutomaticReconnect();
+                    break;
+                }
+                case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
+                    addKeepalivePacketFilter(msg);
+                    break;
+                }
+                case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
+                    removeKeepalivePacketFilter(msg);
+                    break;
+                }
             }
         }
     }
diff --git a/core/java/android/net/NetworkMisc.aidl b/core/java/android/net/NetworkAgentConfig.aidl
similarity index 95%
rename from core/java/android/net/NetworkMisc.aidl
rename to core/java/android/net/NetworkAgentConfig.aidl
index c65583f..cb70bdd 100644
--- a/core/java/android/net/NetworkMisc.aidl
+++ b/core/java/android/net/NetworkAgentConfig.aidl
@@ -16,4 +16,4 @@
 
 package android.net;
 
-parcelable NetworkMisc;
+parcelable NetworkAgentConfig;
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkAgentConfig.java
similarity index 72%
rename from core/java/android/net/NetworkMisc.java
rename to core/java/android/net/NetworkAgentConfig.java
index 4ad52d5..3a383a4 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -26,7 +28,7 @@
  *
  * @hide
  */
-public class NetworkMisc implements Parcelable {
+public class NetworkAgentConfig implements Parcelable {
 
     /**
      * If the {@link Network} is a VPN, whether apps are allowed to bypass the
@@ -83,17 +85,17 @@
      */
     public boolean hasShownBroken;
 
-    public NetworkMisc() {
+    public NetworkAgentConfig() {
     }
 
-    public NetworkMisc(NetworkMisc nm) {
-        if (nm != null) {
-            allowBypass = nm.allowBypass;
-            explicitlySelected = nm.explicitlySelected;
-            acceptUnvalidated = nm.acceptUnvalidated;
-            subscriberId = nm.subscriberId;
-            provisioningNotificationDisabled = nm.provisioningNotificationDisabled;
-            skip464xlat = nm.skip464xlat;
+    public NetworkAgentConfig(@Nullable NetworkAgentConfig nac) {
+        if (nac != null) {
+            allowBypass = nac.allowBypass;
+            explicitlySelected = nac.explicitlySelected;
+            acceptUnvalidated = nac.acceptUnvalidated;
+            subscriberId = nac.subscriberId;
+            provisioningNotificationDisabled = nac.provisioningNotificationDisabled;
+            skip464xlat = nac.skip464xlat;
         }
     }
 
@@ -112,22 +114,23 @@
         out.writeInt(skip464xlat ? 1 : 0);
     }
 
-    public static final @android.annotation.NonNull Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
+    public static final @NonNull Creator<NetworkAgentConfig> CREATOR =
+            new Creator<NetworkAgentConfig>() {
         @Override
-        public NetworkMisc createFromParcel(Parcel in) {
-            NetworkMisc networkMisc = new NetworkMisc();
-            networkMisc.allowBypass = in.readInt() != 0;
-            networkMisc.explicitlySelected = in.readInt() != 0;
-            networkMisc.acceptUnvalidated = in.readInt() != 0;
-            networkMisc.subscriberId = in.readString();
-            networkMisc.provisioningNotificationDisabled = in.readInt() != 0;
-            networkMisc.skip464xlat = in.readInt() != 0;
-            return networkMisc;
+        public NetworkAgentConfig createFromParcel(Parcel in) {
+            NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
+            networkAgentConfig.allowBypass = in.readInt() != 0;
+            networkAgentConfig.explicitlySelected = in.readInt() != 0;
+            networkAgentConfig.acceptUnvalidated = in.readInt() != 0;
+            networkAgentConfig.subscriberId = in.readString();
+            networkAgentConfig.provisioningNotificationDisabled = in.readInt() != 0;
+            networkAgentConfig.skip464xlat = in.readInt() != 0;
+            return networkAgentConfig;
         }
 
         @Override
-        public NetworkMisc[] newArray(int size) {
-            return new NetworkMisc[size];
+        public NetworkAgentConfig[] newArray(int size) {
+            return new NetworkAgentConfig[size];
         }
     };
 }
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
index 42ab925..824ddb8 100644
--- a/core/java/android/net/NetworkFactory.java
+++ b/core/java/android/net/NetworkFactory.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Build;
@@ -27,7 +28,6 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Protocol;
 
@@ -52,7 +52,7 @@
  * @hide
  **/
 public class NetworkFactory extends Handler {
-    /** @hide */
+    /* TODO: delete when all callers have migrated to NetworkProvider IDs. */
     public static class SerialNumber {
         // Guard used by no network factory.
         public static final int NONE = -1;
@@ -91,8 +91,8 @@
      *            with the same NetworkRequest but an updated score.
      *            Also, network conditions may change for this bearer
      *            allowing for a better score in the future.
-     * msg.arg2 = the serial number of the factory currently responsible for the
-     *            NetworkAgent handling this request, or SerialNumber.NONE if none.
+     * msg.arg2 = the ID of the NetworkProvider currently responsible for the
+     *            NetworkAgent handling this request, or NetworkProvider.ID_NONE if none.
      */
     public static final int CMD_REQUEST_NETWORK = BASE;
 
@@ -124,7 +124,6 @@
 
     private final Context mContext;
     private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>();
-    private AsyncChannel mAsyncChannel;
     private final String LOG_TAG;
 
     private final SparseArray<NetworkRequestInfo> mNetworkRequests =
@@ -135,7 +134,8 @@
 
     private int mRefCount = 0;
     private Messenger mMessenger = null;
-    private int mSerialNumber;
+    private NetworkProvider mProvider = null;
+    private int mProviderId;
 
     @UnsupportedAppUsage
     public NetworkFactory(Looper looper, Context context, String logTag,
@@ -147,55 +147,43 @@
     }
 
     public void register() {
-        if (DBG) log("Registering NetworkFactory");
-        if (mMessenger == null) {
-            mMessenger = new Messenger(this);
-            mSerialNumber = ConnectivityManager.from(mContext).registerNetworkFactory(mMessenger,
-                    LOG_TAG);
+        if (mProvider != null) {
+            Log.e(LOG_TAG, "Ignoring attempt to register already-registered NetworkFactory");
+            return;
         }
+        if (DBG) log("Registering NetworkFactory");
+
+        mProvider = new NetworkProvider(mContext, NetworkFactory.this.getLooper(), LOG_TAG) {
+            @Override
+            public void onNetworkRequested(@NonNull NetworkRequest request, int score,
+                    int servingProviderId) {
+                handleAddRequest((NetworkRequest) request, score, servingProviderId);
+            }
+
+            @Override
+            public void onRequestWithdrawn(@NonNull NetworkRequest request) {
+                handleRemoveRequest(request);
+            }
+        };
+
+        mMessenger = new Messenger(this);
+        mProviderId = ConnectivityManager.from(mContext).registerNetworkProvider(mProvider);
     }
 
     public void unregister() {
-        if (DBG) log("Unregistering NetworkFactory");
-        if (mMessenger != null) {
-            ConnectivityManager.from(mContext).unregisterNetworkFactory(mMessenger);
-            mMessenger = null;
+        if (mProvider == null) {
+            Log.e(LOG_TAG, "Ignoring attempt to unregister unregistered NetworkFactory");
+            return;
         }
+        if (DBG) log("Unregistering NetworkFactory");
+
+        ConnectivityManager.from(mContext).unregisterNetworkProvider(mProvider);
+        mProvider = null;
     }
 
     @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
-            case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
-                if (mAsyncChannel != null) {
-                    log("Received new connection while already connected!");
-                    break;
-                }
-                if (VDBG) log("NetworkFactory fully connected");
-                AsyncChannel ac = new AsyncChannel();
-                ac.connected(null, this, msg.replyTo);
-                ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
-                        AsyncChannel.STATUS_SUCCESSFUL);
-                mAsyncChannel = ac;
-                for (Message m : mPreConnectedQueue) {
-                    ac.sendMessage(m);
-                }
-                mPreConnectedQueue.clear();
-                break;
-            }
-            case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
-                if (VDBG) log("CMD_CHANNEL_DISCONNECT");
-                if (mAsyncChannel != null) {
-                    mAsyncChannel.disconnect();
-                    mAsyncChannel = null;
-                }
-                break;
-            }
-            case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
-                if (DBG) log("NetworkFactory channel lost");
-                mAsyncChannel = null;
-                break;
-            }
             case CMD_REQUEST_NETWORK: {
                 handleAddRequest((NetworkRequest) msg.obj, msg.arg1, msg.arg2);
                 break;
@@ -219,13 +207,13 @@
         public final NetworkRequest request;
         public int score;
         public boolean requested; // do we have a request outstanding, limited by score
-        public int factorySerialNumber;
+        public int providerId;
 
-        NetworkRequestInfo(NetworkRequest request, int score, int factorySerialNumber) {
+        NetworkRequestInfo(NetworkRequest request, int score, int providerId) {
             this.request = request;
             this.score = score;
             this.requested = false;
-            this.factorySerialNumber = factorySerialNumber;
+            this.providerId = providerId;
         }
 
         @Override
@@ -240,8 +228,6 @@
      *
      * @param request the request to handle.
      * @param score the score of the NetworkAgent currently satisfying this request.
-     * @param servingFactorySerialNumber the serial number of the NetworkFactory that
-     *         created the NetworkAgent currently satisfying this request.
      */
     // 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
@@ -249,7 +235,7 @@
     // the entire tree.
     @VisibleForTesting
     protected void handleAddRequest(NetworkRequest request, int score) {
-        handleAddRequest(request, score, SerialNumber.NONE);
+        handleAddRequest(request, score, NetworkProvider.ID_NONE);
     }
 
     /**
@@ -258,27 +244,26 @@
      *
      * @param request the request to handle.
      * @param score the score of the NetworkAgent currently satisfying this request.
-     * @param servingFactorySerialNumber the serial number of the NetworkFactory that
-     *         created the NetworkAgent currently satisfying this request.
+     * @param servingProviderId the ID of the NetworkProvider that created the NetworkAgent
+     *        currently satisfying this request.
      */
     @VisibleForTesting
-    protected void handleAddRequest(NetworkRequest request, int score,
-            int servingFactorySerialNumber) {
+    protected void handleAddRequest(NetworkRequest request, int score, int servingProviderId) {
         NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
         if (n == null) {
             if (DBG) {
                 log("got request " + request + " with score " + score
-                        + " and serial " + servingFactorySerialNumber);
+                        + " and providerId " + servingProviderId);
             }
-            n = new NetworkRequestInfo(request, score, servingFactorySerialNumber);
+            n = new NetworkRequestInfo(request, score, servingProviderId);
             mNetworkRequests.put(n.request.requestId, n);
         } else {
             if (VDBG) {
                 log("new score " + score + " for exisiting request " + request
-                        + " with serial " + servingFactorySerialNumber);
+                        + " and providerId " + servingProviderId);
             }
             n.score = score;
-            n.factorySerialNumber = servingFactorySerialNumber;
+            n.providerId = servingProviderId;
         }
         if (VDBG) log("  my score=" + mScore + ", my filter=" + mCapabilityFilter);
 
@@ -333,8 +318,8 @@
             log(" n.requests = " + n.requested);
             log(" n.score = " + n.score);
             log(" mScore = " + mScore);
-            log(" n.factorySerialNumber = " + n.factorySerialNumber);
-            log(" mSerialNumber = " + mSerialNumber);
+            log(" n.providerId = " + n.providerId);
+            log(" mProviderId = " + mProviderId);
         }
         if (shouldNeedNetworkFor(n)) {
             if (VDBG) log("  needNetworkFor");
@@ -355,7 +340,7 @@
             // If the score of this request is higher or equal to that of this factory and some
             // other factory is responsible for it, then this factory should not track the request
             // because it has no hope of satisfying it.
-            && (n.score < mScore || n.factorySerialNumber == mSerialNumber)
+            && (n.score < mScore || n.providerId == mProviderId)
             // If this factory can't satisfy the capability needs of this request, then it
             // should not be tracked.
             && n.request.networkCapabilities.satisfiedByNetworkCapabilities(mCapabilityFilter)
@@ -373,7 +358,7 @@
             //   assigned to the factory
             // - This factory can't satisfy the capability needs of the request
             // - The concrete implementation of the factory rejects the request
-            && ((n.score > mScore && n.factorySerialNumber != mSerialNumber)
+            && ((n.score > mScore && n.providerId != mProviderId)
                     || !n.request.networkCapabilities.satisfiedByNetworkCapabilities(
                             mCapabilityFilter)
                     || !acceptRequest(n.request, n.score));
@@ -408,12 +393,7 @@
     protected void releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest r) {
         post(() -> {
             if (DBG) log("releaseRequestAsUnfulfillableByAnyFactory: " + r);
-            Message msg = obtainMessage(EVENT_UNFULFILLABLE_REQUEST, r);
-            if (mAsyncChannel != null) {
-                mAsyncChannel.sendMessage(msg);
-            } else {
-                mPreConnectedQueue.add(msg);
-            }
+            ConnectivityManager.from(mContext).declareNetworkRequestUnfulfillable(r);
         });
     }
 
@@ -444,8 +424,13 @@
         return mNetworkRequests.size();
     }
 
+    /* TODO: delete when all callers have migrated to NetworkProvider IDs. */
     public int getSerialNumber() {
-        return mSerialNumber;
+        return mProviderId;
+    }
+
+    public int getProviderId() {
+        return mProviderId;
     }
 
     protected void log(String s) {
@@ -465,8 +450,8 @@
 
     @Override
     public String toString() {
-        StringBuilder sb = new StringBuilder("{").append(LOG_TAG).append(" - mSerialNumber=")
-                .append(mSerialNumber).append(", ScoreFilter=")
+        StringBuilder sb = new StringBuilder("{").append(LOG_TAG).append(" - mProviderId=")
+                .append(mProviderId).append(", ScoreFilter=")
                 .append(mScore).append(", Filter=").append(mCapabilityFilter).append(", requests=")
                 .append(mNetworkRequests.size()).append(", refCount=").append(mRefCount)
                 .append("}");
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index b0c2546..ac70b52 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -631,10 +631,12 @@
         }
     }
 
-    private static void sendDeathNotice(DeathRecipient recipient) {
-        if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
+    private static void sendDeathNotice(DeathRecipient recipient, IBinder binderProxy) {
+        if (false) {
+            Log.v("JavaBinder", "sendDeathNotice to " + recipient + " for " + binderProxy);
+        }
         try {
-            recipient.binderDied();
+            recipient.binderDied(binderProxy);
         } catch (RuntimeException exc) {
             Log.w("BinderNative", "Uncaught exception from death notification",
                     exc);
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index f336fda..f5fe9c3 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -285,6 +285,13 @@
      */
     public interface DeathRecipient {
         public void binderDied();
+
+        /**
+         * @hide
+         */
+        default void binderDied(IBinder who) {
+            binderDied();
+        }
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a31c3d1..548b123 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -510,28 +510,31 @@
             "android.settings.WIFI_IP_SETTINGS";
 
     /**
-     * Activity Action: Show setting page to process an Easy Connect (Wi-Fi DPP) QR code and start
+     * Activity Action: Show setting page to process a Wi-Fi Easy Connect (aka DPP) URI and start
      * configuration. This intent should be used when you want to use this device to take on the
-     * configurator role for an IoT/other device. When provided with a valid DPP URI string Settings
-     * will open a wifi selection screen for the user to indicate which network they would like to
-     * configure the device specified in the DPP URI string for and carry them through the rest of
-     * the flow for provisioning the device.
+     * configurator role for an IoT/other device. When provided with a valid DPP URI
+     * string, Settings will open a Wi-Fi selection screen for the user to indicate which network
+     * they would like to configure the device specified in the DPP URI string and
+     * carry them through the rest of the flow for provisioning the device.
      * <p>
-     * In some cases, a matching Activity may not exist, so ensure you safeguard against this by
-     * checking WifiManager.isEasyConnectSupported();
+     * In some cases, a matching Activity may not exist, so ensure to safeguard against this by
+     * checking {@link WifiManager#isEasyConnectSupported()}.
      * <p>
      * Input: The Intent's data URI specifies bootstrapping information for authenticating and
-     * provisioning the peer, with the "DPP" scheme.
+     * provisioning the peer, and uses a "DPP" scheme. The URI should be attached to the intent
+     * using {@link Intent#setData(Uri)}. The calling app can obtain a DPP URI in any
+     * way, e.g. by scanning a QR code or other out-of-band methods.
      * <p>
-     * Output: After {@code startActivityForResult}, the callback {@code onActivityResult} will have
-     * resultCode {@link android.app.Activity#RESULT_OK} if Wi-Fi Easy Connect configuration succeeded
-     * and the user tapped 'Done' button, or {@link android.app.Activity#RESULT_CANCELED} if operation
-     * failed and user tapped 'Cancel'. In case the operation has failed, a status code from {@link
-     * android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode} will be returned as
-     * Extra {@link #EXTRA_EASY_CONNECT_ERROR_CODE}. Easy Connect R2 Enrollees report additional
-     * details about the error they encountered, which will be provided in the {@link
-     * #EXTRA_EASY_CONNECT_ATTEMPTED_SSID}, {@link #EXTRA_EASY_CONNECT_CHANNEL_LIST}, and {@link
-     * #EXTRA_EASY_CONNECT_BAND_LIST}.
+     * Output: After calling {@link android.app.Activity#startActivityForResult}, the callback
+     * {@code onActivityResult} will have resultCode {@link android.app.Activity#RESULT_OK} if
+     * the Wi-Fi Easy Connect configuration succeeded and the user tapped the 'Done' button, or
+     * {@link android.app.Activity#RESULT_CANCELED} if the operation failed and user tapped the
+     * 'Cancel' button. In case the operation has failed, a status code from
+     * {@link android.net.wifi.EasyConnectStatusCallback} {@code EASY_CONNECT_EVENT_FAILURE_*} will
+     * be returned as an Extra {@link #EXTRA_EASY_CONNECT_ERROR_CODE}. Easy Connect R2
+     * Enrollees report additional details about the error they encountered, which will be
+     * provided in the {@link #EXTRA_EASY_CONNECT_ATTEMPTED_SSID},
+     * {@link #EXTRA_EASY_CONNECT_CHANNEL_LIST}, and {@link #EXTRA_EASY_CONNECT_BAND_LIST}.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_URI =
@@ -540,12 +543,15 @@
     /**
      * Activity Extra: The Easy Connect operation error code
      * <p>
-     * An extra returned on the result intent received when using the {@link
-     * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This
-     * extra contains the error code of the operation - one of
-     * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode}.
-     * If there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK},
+     * An extra returned on the result intent received when using the
+     * {@link #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation.
+     * This extra contains the integer error code of the operation - one of
+     * {@link android.net.wifi.EasyConnectStatusCallback} {@code EASY_CONNECT_EVENT_FAILURE_*}. If
+     * there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK},
      * then this extra is not attached to the result intent.
+     * <p>
+     * Use the {@link Intent#hasExtra(String)} to determine whether the extra is attached and
+     * {@link Intent#getIntExtra(String, int)} to obtain the error code data.
      */
     public static final String EXTRA_EASY_CONNECT_ERROR_CODE =
             "android.provider.extra.EASY_CONNECT_ERROR_CODE";
@@ -557,11 +563,13 @@
      * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This
      * extra contains the SSID of the Access Point that the remote Enrollee tried to connect to.
      * This value is populated only by remote R2 devices, and only for the following error codes:
-     * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}
-     * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION}.
+     * {@link android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}
+     * {@link android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION}.
      * Therefore, always check if this extra is available using {@link Intent#hasExtra(String)}. If
      * there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, then
      * this extra is not attached to the result intent.
+     * <p>
+     * Use the {@link Intent#getStringExtra(String)} to obtain the SSID.
      */
     public static final String EXTRA_EASY_CONNECT_ATTEMPTED_SSID =
             "android.provider.extra.EASY_CONNECT_ATTEMPTED_SSID";
@@ -571,13 +579,15 @@
      * <p>
      * An extra returned on the result intent received when using the {@link
      * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This
-     * extra contains the list channels the Enrollee used to scan for a network. This value is
+     * extra contains the channel list that the Enrollee scanned for a network. This value is
      * populated only by remote R2 devices, and only for the following error code: {@link
-     * android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}.
+     * android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}.
      * Therefore, always check if this extra is available using {@link Intent#hasExtra(String)}. If
      * there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, then
      * this extra is not attached to the result intent. The list is JSON formatted, as an array
      * (Wi-Fi global operating classes) of arrays (Wi-Fi channels).
+     * <p>
+     * Use the {@link Intent#getStringExtra(String)} to obtain the list.
      */
     public static final String EXTRA_EASY_CONNECT_CHANNEL_LIST =
             "android.provider.extra.EASY_CONNECT_CHANNEL_LIST";
@@ -588,14 +598,16 @@
      * An extra returned on the result intent received when using the {@link
      * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This
      * extra contains the bands the Enrollee supports, expressed as the Global Operating Class,
-     * see Table E-4 in IEEE Std 802.11-2016 -Global operating classes. This value is populated only
+     * see Table E-4 in IEEE Std 802.11-2016 Global operating classes. This value is populated only
      * by remote R2 devices, and only for the following error codes: {@link
-     * android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}
-     * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION}
-     * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION}.
+     * android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}
+     * {@link android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION}
+     * {@link android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION}.
      * Therefore, always check if this extra is available using {@link Intent#hasExtra(String)}. If
      * there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, then
      * this extra is not attached to the result intent.
+     * <p>
+     * Use the {@link Intent#getIntArrayExtra(String)} to obtain the list.
      */
     public static final String EXTRA_EASY_CONNECT_BAND_LIST =
             "android.provider.extra.EASY_CONNECT_BAND_LIST";
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 2e7ac3f5..f369064 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4142,7 +4142,6 @@
          *     <li>{@link #ENABLE_CMAS_PRESIDENTIAL_PREF}</li>
          *     <li>{@link #ENABLE_ALERT_VIBRATION_PREF}</li>
          *     <li>{@link #ENABLE_EMERGENCY_PERF}</li>
-         *     <li>{@link #ENABLE_FULL_VOLUME_PREF}</li>
          *     <li>{@link #ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF}</li>
          * </ul>
          * @hide
@@ -4205,10 +4204,6 @@
             public static final @NonNull String ENABLE_EMERGENCY_PERF =
                     "enable_emergency_alerts";
 
-            /** Preference to enable volume for alerts */
-            public static final @NonNull String ENABLE_FULL_VOLUME_PREF =
-                    "use_full_volume";
-
             /** Preference to enable receive alerts in second language */
             public static final @NonNull String ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF =
                     "receive_cmas_in_second_language";
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index d646e23..00060ab 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -22,14 +22,11 @@
 
 package android.se.omapi;
 
-import android.app.ActivityThread;
 import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -143,10 +140,6 @@
             throw new NullPointerException("Arguments must not be null");
         }
 
-        if (!hasOMAPIReaders()) {
-            throw new UnsupportedOperationException("Device does not support any OMAPI reader");
-        }
-
         mContext = context;
         mSEListener.mListener = listener;
         mSEListener.mExecutor = executor;
@@ -277,23 +270,4 @@
             throw new IllegalStateException(e.getMessage());
         }
     }
-
-    /**
-     * Helper to check if this device support any OMAPI readers
-     */
-    private static boolean hasOMAPIReaders() {
-        IPackageManager pm = ActivityThread.getPackageManager();
-        if (pm == null) {
-            Log.e(TAG, "Cannot get package manager, assuming OMAPI readers supported");
-            return true;
-        }
-        try {
-            return pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC, 0)
-                || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_ESE, 0)
-                || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_SD, 0);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Package manager query failed, assuming OMAPI readers supported", e);
-            return true;
-        }
-    }
 }
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index d827b30..262d989 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.IntentSender;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -238,6 +239,7 @@
         public Builder(@NonNull RemoteViews presentation,
                 @NonNull InlinePresentation inlinePresentation) {
             Preconditions.checkNotNull(presentation, "presentation must be non-null");
+            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
             mPresentation = presentation;
             mInlinePresentation = inlinePresentation;
         }
@@ -248,7 +250,8 @@
          * @param presentation The presentation used to visualize this dataset.
          */
         public Builder(@NonNull RemoteViews presentation) {
-            this(presentation, null);
+            Preconditions.checkNotNull(presentation, "presentation must be non-null");
+            mPresentation = presentation;
         }
 
         /**
@@ -262,7 +265,9 @@
          * @hide
          */
         @SystemApi
+        @TestApi
         public Builder(@NonNull InlinePresentation inlinePresentation) {
+            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
             mInlinePresentation = inlinePresentation;
         }
 
@@ -576,6 +581,7 @@
          * @hide
          */
         @SystemApi
+        @TestApi
         public @NonNull Builder setInlinePresentation(@NonNull AutofillId id,
                 @Nullable AutofillValue value, @Nullable Pattern filter,
                 @NonNull InlinePresentation inlinePresentation) {
@@ -672,11 +678,13 @@
             // using specially crafted parcels.
             final RemoteViews presentation = parcel.readParcelable(null);
             final InlinePresentation inlinePresentation = parcel.readParcelable(null);
-            final Builder builder = presentation == null
-                    ? new Builder(inlinePresentation)
-                    : inlinePresentation == null
+            final Builder builder = presentation != null
+                    ? inlinePresentation == null
                             ? new Builder(presentation)
-                            : new Builder(presentation, inlinePresentation);
+                            : new Builder(presentation, inlinePresentation)
+                    : inlinePresentation == null
+                            ? new Builder()
+                            : new Builder(inlinePresentation);
             final ArrayList<AutofillId> ids =
                     parcel.createTypedArrayList(AutofillId.CREATOR);
             final ArrayList<AutofillValue> values =
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index d7c6d0f..0f33998 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -119,7 +119,9 @@
     @IntDef(flag = true, prefix = { "RECOGNITION_FLAG_" }, value = {
             RECOGNITION_FLAG_NONE,
             RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO,
-            RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS
+            RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS,
+            RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION,
+            RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION,
     })
     public @interface RecognitionFlags {}
 
@@ -144,6 +146,26 @@
      */
     public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 0x2;
 
+    /**
+     * Audio capabilities flag for {@link #startRecognition(int)} that indicates
+     * if the underlying recognition should use AEC.
+     * This capability may or may not be supported by the system, and support can be queried
+     * by calling {@link #getSupportedAudioCapabilities()}. The corresponding capabilities field for
+     * this flag is {@link #AUDIO_CAPABILITY_ECHO_CANCELLATION}. If this flag is passed without the
+     * audio capability supported, there will be no audio effect applied.
+     */
+    public static final int RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION = 0x4;
+
+    /**
+     * Audio capabilities flag for {@link #startRecognition(int)} that indicates
+     * if the underlying recognition should use noise suppression.
+     * This capability may or may not be supported by the system, and support can be queried
+     * by calling {@link #getSupportedAudioCapabilities()}. The corresponding capabilities field for
+     * this flag is {@link #AUDIO_CAPABILITY_NOISE_SUPPRESSION}. If this flag is passed without the
+     * audio capability supported, there will be no audio effect applied.
+     */
+    public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 0x8;
+
     //---- Recognition mode flags. Return codes for getSupportedRecognitionModes() ----//
     // Must be kept in sync with the related attribute defined as searchKeyphraseRecognitionFlags.
 
@@ -168,6 +190,30 @@
     public static final int RECOGNITION_MODE_USER_IDENTIFICATION
             = SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
 
+    //-- Audio capabilities. Values in returned bit field for getSupportedAudioCapabilities() --//
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "AUDIO_CAPABILITY_" }, value = {
+            AUDIO_CAPABILITY_ECHO_CANCELLATION,
+            AUDIO_CAPABILITY_NOISE_SUPPRESSION,
+    })
+    public @interface AudioCapabilities {}
+
+    /**
+     * If set the underlying module supports AEC.
+     * Returned by {@link #getSupportedAudioCapabilities()}
+     */
+    public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION =
+            SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION;
+
+    /**
+     * If set, the underlying module supports noise suppression.
+     * Returned by {@link #getSupportedAudioCapabilities()}
+     */
+    public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION =
+            SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "MODEL_PARAM_" }, value = {
@@ -448,6 +494,37 @@
     }
 
     /**
+     * Get the audio capabilities supported by the platform which can be enabled when
+     * starting a recognition.
+     *
+     * @see #AUDIO_CAPABILITY_ECHO_CANCELLATION
+     * @see #AUDIO_CAPABILITY_NOISE_SUPPRESSION
+     *
+     * @return Bit field encoding of the AudioCapabilities supported.
+     */
+    @AudioCapabilities
+    public int getSupportedAudioCapabilities() {
+        if (DBG) Slog.d(TAG, "getSupportedAudioCapabilities()");
+        synchronized (mLock) {
+            return getSupportedAudioCapabilitiesLocked();
+        }
+    }
+
+    private int getSupportedAudioCapabilitiesLocked() {
+        try {
+            ModuleProperties properties =
+                    mModelManagementService.getDspModuleProperties(mVoiceInteractionService);
+            if (properties != null) {
+                return properties.audioCapabilities;
+            }
+
+            return 0;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Starts recognition for the associated keyphrase.
      *
      * @see #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
@@ -711,12 +788,21 @@
                 (recognitionFlags&RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO) != 0;
         boolean allowMultipleTriggers =
                 (recognitionFlags&RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS) != 0;
+
+        int audioCapabilities = 0;
+        if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION) != 0) {
+            audioCapabilities |= AUDIO_CAPABILITY_ECHO_CANCELLATION;
+        }
+        if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION) != 0) {
+            audioCapabilities |= AUDIO_CAPABILITY_NOISE_SUPPRESSION;
+        }
+
         int code = STATUS_ERROR;
         try {
             code = mModelManagementService.startRecognition(mVoiceInteractionService,
                     mKeyphraseMetadata.id, mLocale.toLanguageTag(), mInternalCallback,
                     new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
-                            recognitionExtra, null /* additional data */));
+                            recognitionExtra, null /* additional data */, audioCapabilities));
         } catch (RemoteException e) {
             Slog.w(TAG, "RemoteException in startRecognition!", e);
         }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 1b2db36..9d22d30 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -42,6 +42,8 @@
     public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
     public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
     public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
+    public static final String NOTIF_CONVO_BYPASS_SHORTCUT_REQ =
+            "settings_notif_convo_bypass_shortcut_req";
 
     private static final Map<String, String> DEFAULT_FLAGS;
 
@@ -60,6 +62,7 @@
         DEFAULT_FLAGS.put("settings_work_profile", "true");
         DEFAULT_FLAGS.put("settings_controller_loading_enhancement", "false");
         DEFAULT_FLAGS.put("settings_conditionals", "false");
+        DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
     }
 
     /**
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index fa994ba..0892c94 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -16,6 +16,8 @@
 
 package android.util;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -25,172 +27,270 @@
 import android.net.NetworkInfo;
 import android.net.SntpClient;
 import android.os.SystemClock;
-import android.os.TimestampedValue;
 import android.provider.Settings;
 import android.text.TextUtils;
 
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Objects;
+import java.util.function.Supplier;
+
 /**
- * {@link TrustedTime} that connects with a remote NTP server as its trusted
- * time source.
+ * A singleton that connects with a remote NTP server as its trusted time source. This class
+ * is thread-safe. The {@link #forceRefresh()} method is synchronous, i.e. it may occupy the
+ * current thread while performing an NTP request. All other threads calling {@link #forceRefresh()}
+ * will block during that request.
  *
  * @hide
  */
 public class NtpTrustedTime implements TrustedTime {
+
+    /**
+     * The result of a successful NTP query.
+     *
+     * @hide
+     */
+    public static class TimeResult {
+        private final long mTimeMillis;
+        private final long mElapsedRealtimeMillis;
+        private final long mCertaintyMillis;
+
+        public TimeResult(long timeMillis, long elapsedRealtimeMillis, long certaintyMillis) {
+            mTimeMillis = timeMillis;
+            mElapsedRealtimeMillis = elapsedRealtimeMillis;
+            mCertaintyMillis = certaintyMillis;
+        }
+
+        public long getTimeMillis() {
+            return mTimeMillis;
+        }
+
+        public long getElapsedRealtimeMillis() {
+            return mElapsedRealtimeMillis;
+        }
+
+        public long getCertaintyMillis() {
+            return mCertaintyMillis;
+        }
+
+        /** Calculates and returns the current time accounting for the age of this result. */
+        public long currentTimeMillis() {
+            return mTimeMillis + getAgeMillis();
+        }
+
+        /** Calculates and returns the age of this result. */
+        public long getAgeMillis() {
+            return SystemClock.elapsedRealtime() - mElapsedRealtimeMillis;
+        }
+
+        @Override
+        public String toString() {
+            return "TimeResult{"
+                    + "mTimeMillis=" + mTimeMillis
+                    + ", mElapsedRealtimeMillis=" + mElapsedRealtimeMillis
+                    + ", mCertaintyMillis=" + mCertaintyMillis
+                    + '}';
+        }
+    }
+
     private static final String TAG = "NtpTrustedTime";
     private static final boolean LOGD = false;
 
     private static NtpTrustedTime sSingleton;
-    private static Context sContext;
 
-    private final String mServer;
-    private final long mTimeout;
+    @NonNull
+    private final Context mContext;
 
-    private ConnectivityManager mCM;
+    /**
+     * A supplier that returns the ConnectivityManager. The Supplier can return null if
+     * ConnectivityService isn't running yet.
+     */
+    private final Supplier<ConnectivityManager> mConnectivityManagerSupplier =
+            new Supplier<ConnectivityManager>() {
+        private ConnectivityManager mConnectivityManager;
 
-    private boolean mHasCache;
-    private long mCachedNtpTime;
-    private long mCachedNtpElapsedRealtime;
-    private long mCachedNtpCertainty;
+        @Nullable
+        @Override
+        public synchronized ConnectivityManager get() {
+            // We can't do this at initialization time: ConnectivityService might not be running
+            // yet.
+            if (mConnectivityManager == null) {
+                mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
+            }
+            return mConnectivityManager;
+        }
+    };
 
-    private NtpTrustedTime(String server, long timeout) {
-        if (LOGD) Log.d(TAG, "creating NtpTrustedTime using " + server);
-        mServer = server;
-        mTimeout = timeout;
+    // Declared volatile and accessed outside of synchronized blocks to avoid blocking reads during
+    // forceRefresh().
+    private volatile TimeResult mTimeResult;
+
+    private NtpTrustedTime(Context context) {
+        mContext = Objects.requireNonNull(context);
     }
 
     @UnsupportedAppUsage
     public static synchronized NtpTrustedTime getInstance(Context context) {
         if (sSingleton == null) {
-            final Resources res = context.getResources();
-            final ContentResolver resolver = context.getContentResolver();
-
-            final String defaultServer = res.getString(
-                    com.android.internal.R.string.config_ntpServer);
-            final long defaultTimeout = res.getInteger(
-                    com.android.internal.R.integer.config_ntpTimeout);
-
-            final String secureServer = Settings.Global.getString(
-                    resolver, Settings.Global.NTP_SERVER);
-            final long timeout = Settings.Global.getLong(
-                    resolver, Settings.Global.NTP_TIMEOUT, defaultTimeout);
-
-            final String server = secureServer != null ? secureServer : defaultServer;
-            sSingleton = new NtpTrustedTime(server, timeout);
-            sContext = context;
+            Context appContext = context.getApplicationContext();
+            sSingleton = new NtpTrustedTime(appContext);
         }
-
         return sSingleton;
     }
 
-    @Override
     @UnsupportedAppUsage
     public boolean forceRefresh() {
-        // We can't do this at initialization time: ConnectivityService might not be running yet.
         synchronized (this) {
-            if (mCM == null) {
-                mCM = sContext.getSystemService(ConnectivityManager.class);
+            NtpConnectionInfo connectionInfo = getNtpConnectionInfo();
+            if (connectionInfo == null) {
+                // missing server config, so no trusted time available
+                if (LOGD) Log.d(TAG, "forceRefresh: invalid server config");
+                return false;
             }
-        }
 
-        final Network network = mCM == null ? null : mCM.getActiveNetwork();
-        return forceRefresh(network);
-    }
-
-    public boolean forceRefresh(Network network) {
-        if (TextUtils.isEmpty(mServer)) {
-            // missing server, so no trusted time available
-            return false;
-        }
-
-        // We can't do this at initialization time: ConnectivityService might not be running yet.
-        synchronized (this) {
-            if (mCM == null) {
-                mCM = sContext.getSystemService(ConnectivityManager.class);
+            ConnectivityManager connectivityManager = mConnectivityManagerSupplier.get();
+            if (connectivityManager == null) {
+                if (LOGD) Log.d(TAG, "forceRefresh: no ConnectivityManager");
+                return false;
             }
-        }
+            final Network network = connectivityManager.getActiveNetwork();
+            final NetworkInfo ni = connectivityManager.getNetworkInfo(network);
+            if (ni == null || !ni.isConnected()) {
+                if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");
+                return false;
+            }
 
-        final NetworkInfo ni = mCM == null ? null : mCM.getNetworkInfo(network);
-        if (ni == null || !ni.isConnected()) {
-            if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");
-            return false;
-        }
-
-
-        if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
-        final SntpClient client = new SntpClient();
-        if (client.requestTime(mServer, (int) mTimeout, network)) {
-            mHasCache = true;
-            mCachedNtpTime = client.getNtpTime();
-            mCachedNtpElapsedRealtime = client.getNtpTimeReference();
-            mCachedNtpCertainty = client.getRoundTripTime() / 2;
-            return true;
-        } else {
-            return false;
+            if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
+            final SntpClient client = new SntpClient();
+            final String serverName = connectionInfo.getServer();
+            final int timeoutMillis = connectionInfo.getTimeoutMillis();
+            if (client.requestTime(serverName, timeoutMillis, network)) {
+                long ntpCertainty = client.getRoundTripTime() / 2;
+                mTimeResult = new TimeResult(
+                        client.getNtpTime(), client.getNtpTimeReference(), ntpCertainty);
+                return true;
+            } else {
+                return false;
+            }
         }
     }
 
-    @Override
+    /**
+     * Only kept for UnsupportedAppUsage.
+     *
+     * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically.
+     */
+    @Deprecated
     @UnsupportedAppUsage
     public boolean hasCache() {
-        return mHasCache;
+        return mTimeResult != null;
     }
 
+    /**
+     * Only kept for UnsupportedAppUsage.
+     *
+     * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically.
+     */
+    @Deprecated
     @Override
     public long getCacheAge() {
-        if (mHasCache) {
-            return SystemClock.elapsedRealtime() - mCachedNtpElapsedRealtime;
+        TimeResult timeResult = mTimeResult;
+        if (timeResult != null) {
+            return SystemClock.elapsedRealtime() - timeResult.getElapsedRealtimeMillis();
         } else {
             return Long.MAX_VALUE;
         }
     }
 
-    @Override
-    public long getCacheCertainty() {
-        if (mHasCache) {
-            return mCachedNtpCertainty;
-        } else {
-            return Long.MAX_VALUE;
-        }
-    }
-
-    @Override
+    /**
+     * Only kept for UnsupportedAppUsage.
+     *
+     * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically.
+     */
+    @Deprecated
     @UnsupportedAppUsage
     public long currentTimeMillis() {
-        if (!mHasCache) {
+        TimeResult timeResult = mTimeResult;
+        if (timeResult == null) {
             throw new IllegalStateException("Missing authoritative time source");
         }
         if (LOGD) Log.d(TAG, "currentTimeMillis() cache hit");
 
         // current time is age after the last ntp cache; callers who
-        // want fresh values will hit makeAuthoritative() first.
-        return mCachedNtpTime + getCacheAge();
-    }
-
-    @UnsupportedAppUsage
-    public long getCachedNtpTime() {
-        if (LOGD) Log.d(TAG, "getCachedNtpTime() cache hit");
-        return mCachedNtpTime;
-    }
-
-    @UnsupportedAppUsage
-    public long getCachedNtpTimeReference() {
-        return mCachedNtpElapsedRealtime;
+        // want fresh values will hit forceRefresh() first.
+        return timeResult.currentTimeMillis();
     }
 
     /**
-     * Returns the combination of {@link #getCachedNtpTime()} and {@link
-     * #getCachedNtpTimeReference()} as a {@link TimestampedValue}. This method is useful when
-     * passing the time to another component that will adjust for elapsed time.
+     * Only kept for UnsupportedAppUsage.
      *
-     * @throws IllegalStateException if there is no cached value
+     * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically.
      */
-    public TimestampedValue<Long> getCachedNtpTimeSignal() {
-        if (!mHasCache) {
-            throw new IllegalStateException("Missing authoritative time source");
-        }
-        if (LOGD) Log.d(TAG, "getCachedNtpTimeSignal() cache hit");
-
-        return new TimestampedValue<>(mCachedNtpElapsedRealtime, mCachedNtpTime);
+    @Deprecated
+    @UnsupportedAppUsage
+    public long getCachedNtpTime() {
+        if (LOGD) Log.d(TAG, "getCachedNtpTime() cache hit");
+        TimeResult timeResult = mTimeResult;
+        return timeResult == null ? 0 : timeResult.getTimeMillis();
     }
 
+    /**
+     * Only kept for UnsupportedAppUsage.
+     *
+     * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically.
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public long getCachedNtpTimeReference() {
+        TimeResult timeResult = mTimeResult;
+        return timeResult == null ? 0 : timeResult.getElapsedRealtimeMillis();
+    }
+
+    /**
+     * Returns an object containing the latest NTP information available. Can return {@code null} if
+     * no information is available.
+     */
+    @Nullable
+    public TimeResult getCachedTimeResult() {
+        return mTimeResult;
+    }
+
+    private static class NtpConnectionInfo {
+
+        @NonNull private final String mServer;
+        private final int mTimeoutMillis;
+
+        NtpConnectionInfo(@NonNull String server, int timeoutMillis) {
+            mServer = Objects.requireNonNull(server);
+            mTimeoutMillis = timeoutMillis;
+        }
+
+        @NonNull
+        public String getServer() {
+            return mServer;
+        }
+
+        int getTimeoutMillis() {
+            return mTimeoutMillis;
+        }
+    }
+
+    @GuardedBy("this")
+    private NtpConnectionInfo getNtpConnectionInfo() {
+        final ContentResolver resolver = mContext.getContentResolver();
+
+        final Resources res = mContext.getResources();
+        final String defaultServer = res.getString(
+                com.android.internal.R.string.config_ntpServer);
+        final int defaultTimeoutMillis = res.getInteger(
+                com.android.internal.R.integer.config_ntpTimeout);
+
+        final String secureServer = Settings.Global.getString(
+                resolver, Settings.Global.NTP_SERVER);
+        final int timeoutMillis = Settings.Global.getInt(
+                resolver, Settings.Global.NTP_TIMEOUT, defaultTimeoutMillis);
+
+        final String server = secureServer != null ? secureServer : defaultServer;
+        return TextUtils.isEmpty(server) ? null : new NtpConnectionInfo(server, timeoutMillis);
+    }
 }
diff --git a/core/java/android/util/TrustedTime.java b/core/java/android/util/TrustedTime.java
index 1360f87..f41fe85 100644
--- a/core/java/android/util/TrustedTime.java
+++ b/core/java/android/util/TrustedTime.java
@@ -20,42 +20,48 @@
 
 /**
  * Interface that provides trusted time information, possibly coming from an NTP
- * server. Implementations may cache answers until {@link #forceRefresh()}.
+ * server.
  *
  * @hide
+ * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime}
  */
 public interface TrustedTime {
     /**
      * Force update with an external trusted time source, returning {@code true}
      * when successful.
+     *
+     * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime}
      */
+    @Deprecated
     @UnsupportedAppUsage
     public boolean forceRefresh();
 
     /**
      * Check if this instance has cached a response from a trusted time source.
+     *
+     * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime}
      */
+    @Deprecated
     @UnsupportedAppUsage
-    public boolean hasCache();
+    boolean hasCache();
 
     /**
      * Return time since last trusted time source contact, or
      * {@link Long#MAX_VALUE} if never contacted.
+     *
+     * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime}
      */
+    @Deprecated
     @UnsupportedAppUsage
     public long getCacheAge();
 
     /**
-     * Return certainty of cached trusted time in milliseconds, or
-     * {@link Long#MAX_VALUE} if never contacted. Smaller values are more
-     * precise.
-     */
-    public long getCacheCertainty();
-
-    /**
      * Return current time similar to {@link System#currentTimeMillis()},
      * possibly using a cached authoritative time source.
+     *
+     * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime}
      */
+    @Deprecated
     @UnsupportedAppUsage
-    public long currentTimeMillis();
+    long currentTimeMillis();
 }
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 386c9cb..860ce90 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -17,6 +17,8 @@
 package android.view.inputmethod;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityThread;
 import android.os.Parcelable;
 import android.view.inline.InlinePresentationSpec;
 
@@ -49,6 +51,21 @@
      */
     private final @NonNull List<InlinePresentationSpec> mPresentationSpecs;
 
+    /**
+     * The package name of the app that requests for the inline suggestions and will host the
+     * embedded suggestion views. The app does not have to set the value for the field because
+     * it'll be set by the system for safety reasons.
+     */
+    private @NonNull String mHostPackageName;
+
+    /**
+     * @hide
+     * @see {@link #mHostPackageName}.
+     */
+    public void setHostPackageName(@NonNull String hostPackageName) {
+        mHostPackageName = hostPackageName;
+    }
+
     private void onConstructed() {
         Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size());
     }
@@ -57,9 +74,15 @@
         return SUGGESTION_COUNT_UNLIMITED;
     }
 
+    private static String defaultHostPackageName() {
+        return ActivityThread.currentPackageName();
+    }
+
     /** @hide */
     abstract static class BaseBuilder {
         abstract Builder setPresentationSpecs(@NonNull List<InlinePresentationSpec> value);
+
+        abstract Builder setHostPackageName(@Nullable String value);
     }
 
 
@@ -80,11 +103,15 @@
     @DataClass.Generated.Member
     /* package-private */ InlineSuggestionsRequest(
             int maxSuggestionCount,
-            @NonNull List<InlinePresentationSpec> presentationSpecs) {
+            @NonNull List<InlinePresentationSpec> presentationSpecs,
+            @NonNull String hostPackageName) {
         this.mMaxSuggestionCount = maxSuggestionCount;
         this.mPresentationSpecs = presentationSpecs;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mPresentationSpecs);
+        this.mHostPackageName = hostPackageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mHostPackageName);
 
         onConstructed();
     }
@@ -108,6 +135,16 @@
         return mPresentationSpecs;
     }
 
+    /**
+     * The package name of the app that requests for the inline suggestions and will host the
+     * embedded suggestion views. The app does not have to set the value for the field because
+     * it'll be set by the system for safety reasons.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getHostPackageName() {
+        return mHostPackageName;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -116,13 +153,14 @@
 
         return "InlineSuggestionsRequest { " +
                 "maxSuggestionCount = " + mMaxSuggestionCount + ", " +
-                "presentationSpecs = " + mPresentationSpecs +
+                "presentationSpecs = " + mPresentationSpecs + ", " +
+                "hostPackageName = " + mHostPackageName +
         " }";
     }
 
     @Override
     @DataClass.Generated.Member
-    public boolean equals(@android.annotation.Nullable Object o) {
+    public boolean equals(@Nullable Object o) {
         // You can override field equality logic by defining either of the methods like:
         // boolean fieldNameEquals(InlineSuggestionsRequest other) { ... }
         // boolean fieldNameEquals(FieldType otherValue) { ... }
@@ -134,7 +172,8 @@
         //noinspection PointlessBooleanExpression
         return true
                 && mMaxSuggestionCount == that.mMaxSuggestionCount
-                && java.util.Objects.equals(mPresentationSpecs, that.mPresentationSpecs);
+                && java.util.Objects.equals(mPresentationSpecs, that.mPresentationSpecs)
+                && java.util.Objects.equals(mHostPackageName, that.mHostPackageName);
     }
 
     @Override
@@ -146,6 +185,7 @@
         int _hash = 1;
         _hash = 31 * _hash + mMaxSuggestionCount;
         _hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpecs);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mHostPackageName);
         return _hash;
     }
 
@@ -157,6 +197,7 @@
 
         dest.writeInt(mMaxSuggestionCount);
         dest.writeParcelableList(mPresentationSpecs, flags);
+        dest.writeString(mHostPackageName);
     }
 
     @Override
@@ -173,11 +214,15 @@
         int maxSuggestionCount = in.readInt();
         List<InlinePresentationSpec> presentationSpecs = new ArrayList<>();
         in.readParcelableList(presentationSpecs, InlinePresentationSpec.class.getClassLoader());
+        String hostPackageName = in.readString();
 
         this.mMaxSuggestionCount = maxSuggestionCount;
         this.mPresentationSpecs = presentationSpecs;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mPresentationSpecs);
+        this.mHostPackageName = hostPackageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mHostPackageName);
 
         onConstructed();
     }
@@ -205,6 +250,7 @@
 
         private int mMaxSuggestionCount;
         private @NonNull List<InlinePresentationSpec> mPresentationSpecs;
+        private @NonNull String mHostPackageName;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -260,22 +306,40 @@
             return this;
         }
 
+        /**
+         * The package name of the app that requests for the inline suggestions and will host the
+         * embedded suggestion views. The app does not have to set the value for the field because
+         * it'll be set by the system for safety reasons.
+         */
+        @DataClass.Generated.Member
+        @Override
+        @NonNull Builder setHostPackageName(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mHostPackageName = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull InlineSuggestionsRequest build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x4; // Mark builder used
+            mBuilderFieldsSet |= 0x8; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mMaxSuggestionCount = defaultMaxSuggestionCount();
             }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mHostPackageName = defaultHostPackageName();
+            }
             InlineSuggestionsRequest o = new InlineSuggestionsRequest(
                     mMaxSuggestionCount,
-                    mPresentationSpecs);
+                    mPresentationSpecs,
+                    mHostPackageName);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x4) != 0) {
+            if ((mBuilderFieldsSet & 0x8) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -283,10 +347,10 @@
     }
 
     @DataClass.Generated(
-            time = 1576637222199L,
+            time = 1578948035951L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
-            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\npublic  void setHostPackageName(java.lang.String)\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/com/android/ims/internal/uce/common/CapInfo.java b/core/java/com/android/ims/internal/uce/common/CapInfo.java
index c45a3a4..2bb3f1f 100644
--- a/core/java/com/android/ims/internal/uce/common/CapInfo.java
+++ b/core/java/com/android/ims/internal/uce/common/CapInfo.java
@@ -64,6 +64,20 @@
     private boolean mRcsIpVideoCallSupported = false;
     /** RCS IP Video call support .  */
     private boolean mRcsIpVideoOnlyCallSupported = false;
+    /** IP Geo location Push using SMS. */
+    private boolean mGeoSmsSupported = false;
+    /** RCS call composer support. */
+    private boolean mCallComposerSupported = false;
+    /** RCS post-call support. */
+    private boolean mPostCallSupported = false;
+    /** Shared map support. */
+    private boolean mSharedMapSupported = false;
+    /** Shared Sketch supported. */
+    private boolean mSharedSketchSupported = false;
+    /** Chatbot communication support. */
+    private boolean mChatbotSupported = false;
+    /** Chatbot role support. */
+    private boolean mChatbotRoleSupported = false;
     /** List of supported extensions. */
     private String[] mExts = new String[10];
     /** Time used to compute when to query again. */
@@ -386,6 +400,104 @@
         this.mRcsIpVideoOnlyCallSupported = rcsIpVideoOnlyCallSupported;
     }
 
+    /**
+     * Checks whether Geo Push via SMS is supported.
+     */
+    public boolean isGeoSmsSupported() {
+        return mGeoSmsSupported;
+    }
+
+    /**
+     * Sets Geolocation Push via SMS as supported or not supported.
+     */
+    public void setGeoSmsSupported(boolean geoSmsSupported) {
+         this.mGeoSmsSupported = geoSmsSupported;
+    }
+
+    /**
+     * Checks whether RCS call composer is supported.
+     */
+    public boolean isCallComposerSupported() {
+        return mCallComposerSupported;
+    }
+
+    /**
+     * Sets call composer as supported or not supported.
+     */
+    public void setCallComposerSupported(boolean callComposerSupported) {
+        this.mCallComposerSupported = callComposerSupported;
+    }
+
+    /**
+     * Checks whether post call is supported.
+     */
+    public boolean isPostCallSupported(){
+        return mPostCallSupported;
+    }
+
+    /**
+     * Sets post call as supported or not supported.
+     */
+     public void setPostCallSupported(boolean postCallSupported) {
+         this.mPostCallSupported = postCallSupported;
+     }
+
+    /**
+     * Checks whether shared map is supported.
+     */
+    public boolean isSharedMapSupported() {
+        return mSharedMapSupported;
+    }
+
+    /**
+     * Sets shared map as supported or not supported.
+     */
+    public void setSharedMapSupported(boolean sharedMapSupported) {
+        this.mSharedMapSupported = sharedMapSupported;
+    }
+
+    /**
+     * Checks whether shared sketch is supported.
+     */
+    public boolean isSharedSketchSupported() {
+        return mSharedSketchSupported;
+    }
+
+    /**
+     * Sets shared sketch as supported or not supported.
+     */
+    public void setSharedSketchSupported(boolean sharedSketchSupported) {
+        this.mSharedSketchSupported = sharedSketchSupported;
+    }
+
+    /**
+     * Checks whether chatbot communication is supported.
+     */
+    public boolean isChatbotSupported() {
+        return mChatbotSupported;
+    }
+
+    /**
+     * Sets chatbot communication as supported or not supported.
+     */
+    public void setChatbotSupported(boolean chatbotSupported) {
+        this.mChatbotSupported = chatbotSupported;
+    }
+
+    /**
+     * Checks whether chatbot role is supported.
+     */
+    public boolean isChatbotRoleSupported() {
+        return mChatbotRoleSupported;
+    }
+
+    /**
+     * Sets chatbot role as supported or not supported.
+     */
+    public void setChatbotRoleSupported(boolean chatbotRoleSupported) {
+        this.mChatbotRoleSupported = chatbotRoleSupported;
+    }
+
     /** Gets the list of supported extensions. */
     public String[] getExts() {
         return mExts;
@@ -434,6 +546,13 @@
         dest.writeInt(mGeoPushSupported ? 1 : 0);
         dest.writeInt(mSmSupported ? 1 : 0);
         dest.writeInt(mFullSnFGroupChatSupported ? 1 : 0);
+        dest.writeInt(mGeoSmsSupported ? 1 : 0);
+        dest.writeInt(mCallComposerSupported ? 1 : 0);
+        dest.writeInt(mPostCallSupported ? 1 : 0);
+        dest.writeInt(mSharedMapSupported ? 1 : 0);
+        dest.writeInt(mSharedSketchSupported ? 1 : 0);
+        dest.writeInt(mChatbotSupported ? 1 : 0);
+        dest.writeInt(mChatbotRoleSupported ? 1 : 0);
 
         dest.writeInt(mRcsIpVoiceCallSupported ? 1 : 0);
         dest.writeInt(mRcsIpVideoCallSupported ? 1 : 0);
@@ -476,6 +595,13 @@
         mGeoPushSupported = (source.readInt() == 0) ? false : true;
         mSmSupported = (source.readInt() == 0) ? false : true;
         mFullSnFGroupChatSupported = (source.readInt() == 0) ? false : true;
+        mGeoSmsSupported = (source.readInt() == 0) ? false : true;
+        mCallComposerSupported = (source.readInt() == 0) ? false : true;
+        mPostCallSupported = (source.readInt() == 0) ? false : true;
+        mSharedMapSupported = (source.readInt() == 0) ? false : true;
+        mSharedSketchSupported = (source.readInt() == 0) ? false : true;
+        mChatbotSupported = (source.readInt() == 0) ? false : true;
+        mChatbotRoleSupported = (source.readInt() == 0) ? false : true;
 
         mRcsIpVoiceCallSupported = (source.readInt() == 0) ? false : true;
         mRcsIpVideoCallSupported = (source.readInt() == 0) ? false : true;
diff --git a/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java b/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
index a50a22f..fdff86f 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
@@ -47,6 +47,10 @@
     public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN = 8;
     /** Trigger is unknown. */
     public static final int UCE_PRES_PUBLISH_TRIGGER_UNKNOWN = 9;
+    /** Move to 5G NR with VoPS disabled. */
+    public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10;
+    /** Move to 5G NR with VoPS enabled. */
+    public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11;
 
 
 
@@ -113,4 +117,4 @@
     public void readFromParcel(Parcel source) {
         mPublishTriggerType = source.readInt();
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 9e23c28..9fbc1b7 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -350,7 +350,12 @@
      * (boolean) Whether screenshot flow going to the corner (instead of shown in a notification)
      * is enabled.
      */
-    public static final String SCREENSHOT_CORNER_FLOW = "screenshot_corner_flow";
+    public static final String SCREENSHOT_CORNER_FLOW = "enable_screenshot_corner_flow";
+
+    /**
+     * (boolean) Whether scrolling screenshots are enabled.
+     */
+    public static final String SCREENSHOT_SCROLLING_ENABLED = "enable_screenshot_scrolling";
 
     private SystemUiDeviceConfigFlags() {
     }
diff --git a/core/java/com/android/internal/util/ConnectivityUtil.java b/core/java/com/android/internal/util/ConnectivityUtil.java
new file mode 100644
index 0000000..799352b
--- /dev/null
+++ b/core/java/com/android/internal/util/ConnectivityUtil.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+
+/**
+ * Utility methods for common functionality using by different networks.
+ *
+ * @hide
+ */
+public class ConnectivityUtil {
+
+    private static final String TAG = "ConnectivityUtil";
+
+    private final Context mContext;
+    private final AppOpsManager mAppOps;
+    private final UserManager mUserManager;
+
+    public ConnectivityUtil(Context context) {
+        mContext = context;
+        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+    }
+
+    /**
+     * API to determine if the caller has fine/coarse location permission (depending on
+     * config/targetSDK level) and the location mode is enabled for the user. SecurityException is
+     * thrown if the caller has no permission or the location mode is disabled.
+     * @param pkgName package name of the application requesting access
+     * @param featureId The feature in the package
+     * @param uid The uid of the package
+     * @param message A message describing why the permission was checked. Only needed if this is
+     *                not inside of a two-way binder call from the data receiver
+     */
+    public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid,
+            @Nullable String message)
+            throws SecurityException {
+        checkPackage(uid, pkgName);
+
+        // Location mode must be enabled
+        if (!isLocationModeEnabled()) {
+            // Location mode is disabled, scan results cannot be returned
+            throw new SecurityException("Location mode is disabled for the device");
+        }
+
+        // LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
+        // location information.
+        boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName, featureId,
+                uid, /* coarseForTargetSdkLessThanQ */ true, message);
+
+        // If neither caller or app has location access, there is no need to check
+        // any other permissions. Deny access to scan results.
+        if (!canAppPackageUseLocation) {
+            throw new SecurityException("UID " + uid + " has no location permission");
+        }
+        // If the User or profile is current, permission is granted
+        // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+        if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) {
+            throw new SecurityException("UID " + uid + " profile not permitted");
+        }
+    }
+
+    /**
+     * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION or
+     * android.Manifest.permission.ACCESS_COARSE_LOCATION (depending on config/targetSDK level)
+     * and a corresponding app op is allowed for this package and uid.
+     *
+     * @param pkgName PackageName of the application requesting access
+     * @param featureId The feature in the package
+     * @param uid The uid of the package
+     * @param coarseForTargetSdkLessThanQ If true and the targetSDK < Q then will check for COARSE
+     *                                    else (false or targetSDK >= Q) then will check for FINE
+     * @param message A message describing why the permission was checked. Only needed if this is
+     *                not inside of a two-way binder call from the data receiver
+     */
+    public boolean checkCallersLocationPermission(String pkgName, @Nullable String featureId,
+            int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message) {
+        boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid);
+
+        String permissionType = Manifest.permission.ACCESS_FINE_LOCATION;
+        if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
+            // Having FINE permission implies having COARSE permission (but not the reverse)
+            permissionType = Manifest.permission.ACCESS_COARSE_LOCATION;
+        }
+        if (getUidPermission(permissionType, uid)
+                == PackageManager.PERMISSION_DENIED) {
+            return false;
+        }
+
+        // Always checking FINE - even if will not enforce. This will record the request for FINE
+        // so that a location request by the app is surfaced to the user.
+        boolean isFineLocationAllowed = noteAppOpAllowed(
+                AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid, message);
+        if (isFineLocationAllowed) {
+            return true;
+        }
+        if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
+            return noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName, featureId, uid,
+                    message);
+        }
+        return false;
+    }
+
+    /**
+     * Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
+     */
+    public boolean isLocationModeEnabled() {
+        LocationManager locationManager =
+                (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
+        try {
+            return locationManager.isLocationEnabledForUser(UserHandle.of(
+                    getCurrentUser()));
+        } catch (Exception e) {
+            Log.e(TAG, "Failure to get location mode via API, falling back to settings", e);
+            return false;
+        }
+    }
+
+    private boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            if (mContext.getPackageManager().getApplicationInfoAsUser(
+                    packageName, 0,
+                    UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion
+                    < versionCode) {
+                return true;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // In case of exception, assume unknown app (more strict checking)
+            // Note: This case will never happen since checkPackage is
+            // called to verify validity before checking App's version.
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return false;
+    }
+
+    private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId,
+            int uid, @Nullable String message) {
+        return mAppOps.noteOp(op, uid, pkgName, featureId, message) == AppOpsManager.MODE_ALLOWED;
+    }
+
+    private void checkPackage(int uid, String pkgName) throws SecurityException {
+        if (pkgName == null) {
+            throw new SecurityException("Checking UID " + uid + " but Package Name is Null");
+        }
+        mAppOps.checkPackage(uid, pkgName);
+    }
+
+    private boolean isCurrentProfile(int uid) {
+        UserHandle currentUser = UserHandle.of(getCurrentUser());
+        UserHandle callingUser = UserHandle.getUserHandleForUid(uid);
+        return currentUser.equals(callingUser)
+                || mUserManager.isSameProfileGroup(currentUser, callingUser);
+    }
+
+    private boolean checkInteractAcrossUsersFull(int uid) {
+        return getUidPermission(
+                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    @VisibleForTesting
+    protected int getCurrentUser() {
+        return ActivityManager.getCurrentUser();
+    }
+
+    private int getUidPermission(String permissionType, int uid) {
+        // We don't care about pid, pass in -1
+        return mContext.checkPermission(permissionType, -1, uid);
+    }
+}
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
index 43a227a..d7a01c4 100644
--- a/core/java/com/android/internal/widget/RecyclerView.java
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -2011,13 +2011,27 @@
         }
 
         if (!dispatchNestedPreFling(velocityX, velocityY)) {
-            final boolean canScroll = canScrollHorizontal || canScrollVertical;
-            dispatchNestedFling(velocityX, velocityY, canScroll);
+            final View firstChild = mLayout.getChildAt(0);
+            final View lastChild = mLayout.getChildAt(mLayout.getChildCount() - 1);
+            boolean consumed = false;
+            if (velocityY < 0) {
+                consumed = getChildAdapterPosition(firstChild) > 0
+                        || firstChild.getTop() < getPaddingTop();
+            }
+
+            if (velocityY > 0) {
+                consumed = getChildAdapterPosition(lastChild) < mAdapter.getItemCount() - 1
+                        || lastChild.getBottom() > getHeight() - getPaddingBottom();
+            }
+
+            dispatchNestedFling(velocityX, velocityY, consumed);
 
             if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
                 return true;
             }
 
+            final boolean canScroll = canScrollHorizontal || canScrollVertical;
+
             if (canScroll) {
                 velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
                 velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index fa64fd1..adedffd 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -52,42 +52,34 @@
 
 using namespace android;
 
-jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format) {
-    const char* mimeType;
+const char* getMimeType(SkEncodedImageFormat format) {
     switch (format) {
         case SkEncodedImageFormat::kBMP:
-            mimeType = "image/bmp";
-            break;
+            return "image/bmp";
         case SkEncodedImageFormat::kGIF:
-            mimeType = "image/gif";
-            break;
+            return "image/gif";
         case SkEncodedImageFormat::kICO:
-            mimeType = "image/x-ico";
-            break;
+            return "image/x-ico";
         case SkEncodedImageFormat::kJPEG:
-            mimeType = "image/jpeg";
-            break;
+            return "image/jpeg";
         case SkEncodedImageFormat::kPNG:
-            mimeType = "image/png";
-            break;
+            return "image/png";
         case SkEncodedImageFormat::kWEBP:
-            mimeType = "image/webp";
-            break;
+            return "image/webp";
         case SkEncodedImageFormat::kHEIF:
-            mimeType = "image/heif";
-            break;
+            return "image/heif";
         case SkEncodedImageFormat::kWBMP:
-            mimeType = "image/vnd.wap.wbmp";
-            break;
+            return "image/vnd.wap.wbmp";
         case SkEncodedImageFormat::kDNG:
-            mimeType = "image/x-adobe-dng";
-            break;
+            return "image/x-adobe-dng";
         default:
-            mimeType = nullptr;
-            break;
+            return nullptr;
     }
+}
 
+jstring getMimeTypeAsJavaString(JNIEnv* env, SkEncodedImageFormat format) {
     jstring jstr = nullptr;
+    const char* mimeType = getMimeType(format);
     if (mimeType) {
         // NOTE: Caller should env->ExceptionCheck() for OOM
         // (can't check for nullptr as it's a valid return value)
@@ -289,10 +281,9 @@
 
     // Set the options and return if the client only wants the size.
     if (options != NULL) {
-        jstring mimeType = encodedFormatToString(
-                env, (SkEncodedImageFormat)codec->getEncodedFormat());
+        jstring mimeType = getMimeTypeAsJavaString(env, codec->getEncodedFormat());
         if (env->ExceptionCheck()) {
-            return nullObjectReturn("OOM in encodedFormatToString()");
+            return nullObjectReturn("OOM in getMimeTypeAsJavaString()");
         }
         env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
         env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h
index e37c98d..45bffc4 100644
--- a/core/jni/android/graphics/BitmapFactory.h
+++ b/core/jni/android/graphics/BitmapFactory.h
@@ -26,6 +26,6 @@
 extern jclass gBitmapConfig_class;
 extern jmethodID gBitmapConfig_nativeToConfigMethodID;
 
-jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format);
+jstring getMimeTypeAsJavaString(JNIEnv*, SkEncodedImageFormat);
 
 #endif  // _ANDROID_GRAPHICS_BITMAP_FACTORY_H_
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index f18632d..06b4ff8 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -197,7 +197,7 @@
         env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
 
         env->SetObjectField(options, gOptions_mimeFieldID,
-                encodedFormatToString(env, (SkEncodedImageFormat)brd->getEncodedFormat()));
+                getMimeTypeAsJavaString(env, brd->getEncodedFormat()));
         if (env->ExceptionCheck()) {
             return nullObjectReturn("OOM in encodedFormatToString()");
         }
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 627f8f5..a900286 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -475,7 +475,7 @@
 
 static jstring ImageDecoder_nGetMimeType(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
     auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
-    return encodedFormatToString(env, decoder->mCodec->getEncodedFormat());
+    return getMimeTypeAsJavaString(env, decoder->mCodec->getEncodedFormat());
 }
 
 static jobject ImageDecoder_nGetColorSpace(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
diff --git a/media/java/android/media/RouteDiscoveryRequest.aidl b/core/jni/android/graphics/MimeType.h
similarity index 85%
copy from media/java/android/media/RouteDiscoveryRequest.aidl
copy to core/jni/android/graphics/MimeType.h
index 744f656..38a579c 100644
--- a/media/java/android/media/RouteDiscoveryRequest.aidl
+++ b/core/jni/android/graphics/MimeType.h
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
-package android.media;
+#pragma once
 
-parcelable RouteDiscoveryRequest;
+#include "SkEncodedImageFormat.h"
+
+const char* getMimeType(SkEncodedImageFormat);
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 79cf019..53327bc 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2312,6 +2312,48 @@
     return jStatus;
 }
 
+static jint
+android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz,
+        jobject jaa, jobjectArray jDeviceArray)
+{
+    const jsize maxResultSize = env->GetArrayLength(jDeviceArray);
+    // the JNI is always expected to provide us with an array capable of holding enough
+    // devices i.e. the most we ever route a track to. This is preferred over receiving an ArrayList
+    // with reverse JNI to make the array grow as need as this would be less efficient, and some
+    // components call this method often
+    if (jDeviceArray == nullptr || maxResultSize == 0) {
+        ALOGE("%s invalid array to store AudioDeviceAddress", __FUNCTION__);
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique();
+    jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
+    if (jStatus != (jint) AUDIO_JAVA_SUCCESS) {
+        return jStatus;
+    }
+
+    AudioDeviceTypeAddrVector devices;
+    jStatus = check_AudioSystem_Command(
+            AudioSystem::getDevicesForAttributes(*(paa.get()), &devices));
+    if (jStatus != NO_ERROR) {
+        return jStatus;
+    }
+
+    if (devices.size() > maxResultSize) {
+        return AUDIO_JAVA_INVALID_OPERATION;
+    }
+    size_t index = 0;
+    jobject jAudioDeviceAddress = NULL;
+    for (const auto& device : devices) {
+        jStatus = createAudioDeviceAddressFromNative(env, &jAudioDeviceAddress, &device);
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            return jStatus;
+        }
+        env->SetObjectArrayElement(jDeviceArray, index++, jAudioDeviceAddress);
+    }
+    return jStatus;
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] = {
@@ -2395,6 +2437,7 @@
     {"setPreferredDeviceForStrategy", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setPreferredDeviceForStrategy},
     {"removePreferredDeviceForStrategy", "(I)I", (void *)android_media_AudioSystem_removePreferredDeviceForStrategy},
     {"getPreferredDeviceForStrategy", "(I[Landroid/media/AudioDeviceAddress;)I", (void *)android_media_AudioSystem_getPreferredDeviceForStrategy},
+    {"getDevicesForAttributes", "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAddress;)I", (void *)android_media_AudioSystem_getDevicesForAttributes}
 };
 
 static const JNINativeMethod gEventHandlerMethods[] = {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index ff596b4..062b886 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -220,8 +220,12 @@
 
 // ----------------------------------------------------------------------------
 
+static std::unique_ptr<DynamicLibManager> sDynamicLibManager =
+    std::make_unique<DynamicLibManager>();
+
 // Let the opaque type AAssetManager refer to a guarded AssetManager2 instance.
 struct GuardedAssetManager : public ::AAssetManager {
+  GuardedAssetManager() : guarded_assetmanager(sDynamicLibManager.get()) {}
   Guarded<AssetManager2> guarded_assetmanager;
 };
 
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 0992beb..fb8e633 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -537,9 +537,9 @@
         LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this);
         if (mObject != NULL) {
             JNIEnv* env = javavm_to_jnienv(mVM);
-
+            jobject jBinderProxy = javaObjectForIBinder(env, who.promote());
             env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
-                    gBinderProxyOffsets.mSendDeathNotice, mObject);
+                                      gBinderProxyOffsets.mSendDeathNotice, mObject, jBinderProxy);
             if (env->ExceptionCheck()) {
                 jthrowable excep = env->ExceptionOccurred();
                 report_exception(env, excep,
@@ -1532,8 +1532,9 @@
     gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
     gBinderProxyOffsets.mGetInstance = GetStaticMethodIDOrDie(env, clazz, "getInstance",
             "(JJ)Landroid/os/BinderProxy;");
-    gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
-            "(Landroid/os/IBinder$DeathRecipient;)V");
+    gBinderProxyOffsets.mSendDeathNotice =
+            GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
+                                   "(Landroid/os/IBinder$DeathRecipient;Landroid/os/IBinder;)V");
     gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");
 
     clazz = FindClassOrDie(env, "java/lang/Class");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 4a7276c..b47080f 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -263,7 +263,8 @@
     status_t res = ScreenshotClient::capture(displayToken, dataspace,
             ui::PixelFormat::RGBA_8888,
             sourceCrop, width, height,
-            useIdentityTransform, rotation, captureSecureLayers, &buffer, capturedSecureLayers);
+            useIdentityTransform, ui::toRotation(rotation),
+            captureSecureLayers, &buffer, capturedSecureLayers);
     if (res != NO_ERROR) {
         return NULL;
     }
@@ -724,7 +725,8 @@
 
     {
         auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
-        transaction->setDisplayProjection(token, orientation, layerStackRect, displayRect);
+        transaction->setDisplayProjection(token, static_cast<ui::Rotation>(orientation),
+                                          layerStackRect, displayRect);
     }
 }
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 5039213..466544c 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -746,9 +746,6 @@
 
   const userid_t user_id = multiuser_get_user_id(uid);
   const std::string user_source = StringPrintf("/mnt/user/%d", user_id);
-  const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
-  bool isFuse = GetBoolProperty(kPropFuse, false);
-
   // Shell is neither AID_ROOT nor AID_EVERYBODY. Since it equally needs 'execute' access to
   // /mnt/user/0 to 'adb shell ls /sdcard' for instance, we set the uid bit of /mnt/user/0 to
   // AID_SHELL. This gives shell access along with apps running as group everybody (user 0 apps)
@@ -757,9 +754,15 @@
   PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL,
              multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn);
 
+  bool isFuse = GetBoolProperty(kPropFuse, false);
+
   if (isFuse) {
     if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
+      const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
       BindMount(pass_through_source, "/storage", fail_fn);
+    } else if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
+      const std::string installer_source = StringPrintf("/mnt/installer/%d", user_id);
+      BindMount(installer_source, "/storage", fail_fn);
     } else {
       BindMount(user_source, "/storage", fail_fn);
     }
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index b83b31c..cd3887e 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2553,4 +2553,8 @@
     // OS: R
     MANAGE_EXTERNAL_STORAGE = 1822;
 
+    // Open: Settings > DND > People
+    // OS: R
+    DND_PEOPLE = 1823;
+
 }
diff --git a/core/proto/android/server/animationadapter.proto b/core/proto/android/server/animationadapter.proto
index c6925f4..70627ed 100644
--- a/core/proto/android/server/animationadapter.proto
+++ b/core/proto/android/server/animationadapter.proto
@@ -50,7 +50,6 @@
     optional WindowAnimationSpecProto window = 1;
     optional MoveAnimationSpecProto move = 2;
     optional AlphaAnimationSpecProto alpha = 3;
-    optional RotationAnimationSpecProto rotate = 4;
 }
 
 /* represents WindowAnimationSpec */
@@ -77,12 +76,3 @@
     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/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 9054d54..0fca1d1 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -154,4 +154,5 @@
   SET_AUTO_TIME = 127;
   SET_AUTO_TIME_ZONE = 128;
   SET_PACKAGES_PROTECTED = 129;
+  SET_FACTORY_RESET_PROTECTION = 130;
 }
diff --git a/core/proto/android/stats/launcher/Android.bp b/core/proto/android/stats/launcher/Android.bp
index b8fb6ff..976a0b8 100644
--- a/core/proto/android/stats/launcher/Android.bp
+++ b/core/proto/android/stats/launcher/Android.bp
@@ -25,3 +25,16 @@
         "*.proto",
     ],
 }
+
+java_library {
+    name: "launcherprotoslite",
+    proto: {
+        type: "lite",
+        include_dirs: ["external/protobuf/src"],
+    },
+
+    sdk_version: "current",
+    srcs: [
+        "*.proto",
+    ],
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c050146..b78b181 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -445,6 +445,7 @@
 
     <protected-broadcast android:name="android.internal.policy.action.BURN_IN_PROTECTION" />
     <protected-broadcast android:name="android.app.action.SYSTEM_UPDATE_POLICY_CHANGED" />
+    <protected-broadcast android:name="android.app.action.RESET_PROTECTION_POLICY_CHANGED" />
     <protected-broadcast android:name="android.app.action.DEVICE_OWNER_CHANGED" />
     <protected-broadcast android:name="android.app.action.MANAGED_USER_CREATED" />
 
@@ -640,10 +641,6 @@
 
     <protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" />
 
-    <!-- NETWORK_SET_TIME moved from com.android.phone to system server. It should ultimately be
-         removed. -->
-    <protected-broadcast android:name="android.telephony.action.NETWORK_SET_TIME" />
-
     <!-- For tether entitlement recheck-->
     <protected-broadcast
         android:name="com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM" />
@@ -1791,6 +1788,11 @@
         android:label="@string/permlab_preferredPaymentInfo"
         android:protectionLevel="normal" />
 
+    <!-- @SystemApi Allows an internal user to use privileged SecureElement APIs.
+         @hide -->
+    <permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @deprecated This permission used to allow too broad access to sensitive methods and all its
          uses have been replaced by a more appropriate permission. Most uses have been replaced with
          a NETWORK_STACK or NETWORK_SETTINGS check. Please look up the documentation of the
@@ -3384,6 +3386,14 @@
     <permission android:name="android.permission.NOTIFY_TV_INPUTS"
          android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to interact with tuner resources through
+         Tuner Resource Manager.
+         <p>Protection level: signature|privileged
+         <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.TUNER_RESOURCE_ACCESS"
+         android:protectionLevel="signature|privileged" />
+
     <!-- Must be required by a {@link android.media.routing.MediaRouteService}
          to ensure that only the system can interact with it.
          @hide -->
diff --git a/core/res/res/anim/screen_rotate_0_enter.xml b/core/res/res/anim/screen_rotate_0_enter.xml
index 629be7e..93cf365 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 (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.
-  -->
+/*
+** 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.
+*/
+-->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    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" />
+        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_0_exit.xml b/core/res/res/anim/screen_rotate_0_exit.xml
index fa046a0..37d5a411 100644
--- a/core/res/res/anim/screen_rotate_0_exit.xml
+++ b/core/res/res/anim/screen_rotate_0_exit.xml
@@ -1,25 +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.
-  -->
+/*
+** 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.
+*/
+-->
 
 <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/screen_rotation_alpha_out"
-        android:fillEnabled="true"
-        android:fillBefore="true" android:fillAfter="true"
-        android:duration="@android:integer/config_screen_rotation_fade_out" />
+        android:shareInterpolator="false">
 </set>
diff --git a/core/res/res/anim/screen_rotate_0_frame.xml b/core/res/res/anim/screen_rotate_0_frame.xml
new file mode 100644
index 0000000..5ea9bf8
--- /dev/null
+++ b/core/res/res/anim/screen_rotate_0_frame.xml
@@ -0,0 +1,25 @@
+<?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 889a615..688a8d5 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:fillEnabled="true"
-        android:fillBefore="true" android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="@android:integer/config_screen_rotation_total_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" />
 </set>
diff --git a/core/res/res/anim/screen_rotate_180_exit.xml b/core/res/res/anim/screen_rotate_180_exit.xml
index 766fcfa..58a1868 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:fillEnabled="true"
-        android:fillBefore="true" android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="@android:integer/config_screen_rotation_total_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" />
 </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 2cac982..c49ef9c 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/screen_rotation_alpha_out"
+           android:interpolator="@interpolator/decelerate_quint"
            android:fillEnabled="true"
            android:fillBefore="true" android:fillAfter="true"
-           android:duration="@android:integer/config_screen_rotation_fade_out" />
+           android:duration="@android:integer/config_mediumAnimTime" />
 </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 87fd25e..b16d5fc 100644
--- a/core/res/res/anim/screen_rotate_minus_90_enter.xml
+++ b/core/res/res/anim/screen_rotate_minus_90_enter.xml
@@ -18,17 +18,19 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false">
+        android:shareInterpolator="false">
+    <!-- Version for two-phase anim
     <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/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" />
+            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" />
 </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 c3aee14..0927dd3 100644
--- a/core/res/res/anim/screen_rotate_minus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_minus_90_exit.xml
@@ -18,16 +18,26 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false">
+        android:shareInterpolator="false">
+    <!-- Version for two-phase animation
     <rotate android:fromDegrees="0" android:toDegrees="90"
-        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" />
+            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" />
 </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 8849db4..86a8d24 100644
--- a/core/res/res/anim/screen_rotate_plus_90_enter.xml
+++ b/core/res/res/anim/screen_rotate_plus_90_enter.xml
@@ -18,16 +18,19 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false">
+        android:shareInterpolator="false">
+    <!-- Version for two-phase animation
     <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/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" />
+            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" />
 </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 de84c3b..fd786f9 100644
--- a/core/res/res/anim/screen_rotate_plus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_plus_90_exit.xml
@@ -18,16 +18,26 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false">
+        android:shareInterpolator="false">
+    <!-- Version for two-phase animation
     <rotate android:fromDegrees="0" android:toDegrees="-90"
-        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" />
+            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" />
 </set>
diff --git a/core/res/res/interpolator/screen_rotation_alpha_out.xml b/core/res/res/interpolator/screen_rotation_alpha_out.xml
deleted file mode 100644
index 73a37d4..0000000
--- a/core/res/res/interpolator/screen_rotation_alpha_out.xml
+++ /dev/null
@@ -1,22 +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.
-  -->
-
-<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/values/config.xml b/core/res/res/values/config.xml
index 245aed1..6f554f02 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -147,24 +147,6 @@
     <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>
 
@@ -1895,6 +1877,8 @@
     <string name="config_defaultCallRedirection" translatable="false"></string>
     <!-- The name of the package that will hold the call screening role by default. -->
     <string name="config_defaultCallScreening" translatable="false"></string>
+    <!-- The name of the package that will hold the system gallery role. -->
+    <string name="config_systemGallery" translatable="false">com.android.gallery</string>
 
     <!-- Enable/disable default bluetooth profiles:
         HSP_AG, ObexObjectPush, Audio, NAP -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 37c9710..6cf6a68 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3030,6 +3030,8 @@
       <public name="config_defaultCallRedirection" />
       <!-- @hide @SystemApi -->
       <public name="config_defaultCallScreening" />
+      <!-- @hide @SystemApi @TestApi -->
+      <public name="config_systemGallery" />
     </public-group>
 
     <public-group type="bool" first-id="0x01110005">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 973d5f6..9e11749 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1805,6 +1805,7 @@
   <!-- 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" />
@@ -1980,7 +1981,6 @@
   <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" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index caae908..2df6d1c 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -8,6 +8,7 @@
         "EnabledTestApp/src/**/*.java",
         "BinderProxyCountingTestApp/src/**/*.java",
         "BinderProxyCountingTestService/src/**/*.java",
+        "BinderDeathRecipientHelperApp/src/**/*.java",
         "aidl/**/I*.aidl",
     ],
 
@@ -59,7 +60,11 @@
     resource_dirs: ["res"],
     resource_zips: [":FrameworksCoreTests_apks_as_resources"],
 
-    data: [":BstatsTestApp"],
+    data: [
+        ":BstatsTestApp",
+        ":BinderDeathRecipientHelperApp1",
+        ":BinderDeathRecipientHelperApp2",
+    ],
 }
 
 // Rules to copy all the test apks to the intermediate raw resource directory
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 1aea98a..b85a332 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -95,6 +95,7 @@
 
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
     <uses-permission android:name="android.permission.KILL_UID" />
+    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
 
     <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
 
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index b40aa87..ed9d3f5 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -21,6 +21,8 @@
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="FrameworksCoreTests.apk" />
         <option name="test-file-name" value="BstatsTestApp.apk" />
+        <option name="test-file-name" value="BinderDeathRecipientHelperApp1.apk" />
+        <option name="test-file-name" value="BinderDeathRecipientHelperApp2.apk" />
     </target_preparer>
     <option name="test-tag" value="FrameworksCoreTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp b/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp
new file mode 100644
index 0000000..25e4fc3
--- /dev/null
+++ b/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp
@@ -0,0 +1,19 @@
+android_test_helper_app {
+    name: "BinderDeathRecipientHelperApp1",
+
+    srcs: ["**/*.java"],
+
+    sdk_version: "current",
+}
+
+android_test_helper_app {
+    name: "BinderDeathRecipientHelperApp2",
+
+    srcs: ["**/*.java"],
+
+    sdk_version: "current",
+
+    aaptflags: [
+            "--rename-manifest-package com.android.frameworks.coretests.bdr_helper_app2",
+        ],
+}
diff --git a/core/tests/coretests/BinderDeathRecipientHelperApp/AndroidManifest.xml b/core/tests/coretests/BinderDeathRecipientHelperApp/AndroidManifest.xml
new file mode 100644
index 0000000..dbd1774
--- /dev/null
+++ b/core/tests/coretests/BinderDeathRecipientHelperApp/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.bdr_helper_app1">
+
+    <application>
+        <receiver android:name="com.android.frameworks.coretests.bdr_helper_app.TestCommsReceiver"
+                  android:exported="true"/>
+
+    </application>
+</manifest>
diff --git a/core/tests/coretests/BinderDeathRecipientHelperApp/src/com/android/frameworks/coretests/bdr_helper_app/TestCommsReceiver.java b/core/tests/coretests/BinderDeathRecipientHelperApp/src/com/android/frameworks/coretests/bdr_helper_app/TestCommsReceiver.java
new file mode 100644
index 0000000..ab79e69
--- /dev/null
+++ b/core/tests/coretests/BinderDeathRecipientHelperApp/src/com/android/frameworks/coretests/bdr_helper_app/TestCommsReceiver.java
@@ -0,0 +1,51 @@
+/*
+ * 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.frameworks.coretests.bdr_helper_app;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Receiver used to hand off a binder owned by this process to
+ * {@link android.os.BinderDeathRecipientTest}.
+ */
+public class TestCommsReceiver extends BroadcastReceiver {
+    private static final String TAG = TestCommsReceiver.class.getSimpleName();
+    private  static final String PACKAGE_NAME = "com.android.frameworks.coretests.bdr_helper_app";
+
+    public static final String ACTION_GET_BINDER = PACKAGE_NAME + ".action.GET_BINDER";
+    public static final String EXTRA_KEY_BINDER = PACKAGE_NAME + ".EXTRA_BINDER";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        switch (intent.getAction()) {
+            case ACTION_GET_BINDER:
+                final Bundle resultExtras = new Bundle();
+                resultExtras.putBinder(EXTRA_KEY_BINDER, new Binder());
+                setResult(Activity.RESULT_OK, null, resultExtras);
+                break;
+            default:
+                Log.e(TAG, "Unknown action " + intent.getAction());
+                break;
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java b/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java
new file mode 100644
index 0000000..2cce43f
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.bdr_helper_app.TestCommsReceiver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests functionality of {@link android.os.IBinder.DeathRecipient} callbacks.
+ */
+@RunWith(AndroidJUnit4.class)
+public class BinderDeathRecipientTest {
+    private static final String TAG = BinderDeathRecipientTest.class.getSimpleName();
+    private static final String TEST_PACKAGE_NAME_1 =
+            "com.android.frameworks.coretests.bdr_helper_app1";
+    private static final String TEST_PACKAGE_NAME_2 =
+            "com.android.frameworks.coretests.bdr_helper_app2";
+
+    private Context mContext;
+    private Handler mHandler;
+    private ActivityManager mActivityManager;
+    private Set<Pair<IBinder, IBinder.DeathRecipient>> mLinkedDeathRecipients = new ArraySet<>();
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mActivityManager = mContext.getSystemService(ActivityManager.class);
+        mHandler = new Handler(Looper.getMainLooper());
+    }
+
+    private IBinder getNewRemoteBinder(String testPackage) throws InterruptedException {
+        final CountDownLatch resultLatch = new CountDownLatch(1);
+        final AtomicInteger resultCode = new AtomicInteger(Activity.RESULT_CANCELED);
+        final AtomicReference<Bundle> resultExtras = new AtomicReference<>();
+
+        final Intent intent = new Intent(TestCommsReceiver.ACTION_GET_BINDER)
+                .setClassName(testPackage, TestCommsReceiver.class.getName());
+        mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                resultCode.set(getResultCode());
+                resultExtras.set(getResultExtras(true));
+                resultLatch.countDown();
+            }
+        }, mHandler, Activity.RESULT_CANCELED, null, null);
+
+        assertTrue("Request for binder timed out", resultLatch.await(5, TimeUnit.SECONDS));
+        assertEquals(Activity.RESULT_OK, resultCode.get());
+        return resultExtras.get().getBinder(TestCommsReceiver.EXTRA_KEY_BINDER);
+    }
+
+    @Test
+    public void binderDied_noArgs() throws Exception {
+        final IBinder testAppBinder = getNewRemoteBinder(TEST_PACKAGE_NAME_1);
+        final CountDownLatch deathNotificationLatch = new CountDownLatch(1);
+        final IBinder.DeathRecipient simpleDeathRecipient =
+                () -> deathNotificationLatch.countDown();
+        testAppBinder.linkToDeath(simpleDeathRecipient, 0);
+        mLinkedDeathRecipients.add(Pair.create(testAppBinder, simpleDeathRecipient));
+
+        mActivityManager.forceStopPackage(TEST_PACKAGE_NAME_1);
+        assertTrue("Death notification did not arrive",
+                deathNotificationLatch.await(10, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void binderDied_iBinderArg() throws Exception {
+        final IBinder testApp1Binder = getNewRemoteBinder(TEST_PACKAGE_NAME_1);
+        final IBinder testApp2Binder = getNewRemoteBinder(TEST_PACKAGE_NAME_2);
+        final CyclicBarrier barrier = new CyclicBarrier(2);
+
+        final AtomicReference<IBinder> binderThatDied = new AtomicReference<>();
+        final IBinder.DeathRecipient sameDeathRecipient = new IBinder.DeathRecipient() {
+            @Override
+            public void binderDied() {
+                Log.e(TAG, "Should not have been called!");
+            }
+
+            @Override
+            public void binderDied(IBinder who) {
+                binderThatDied.set(who);
+                try {
+                    barrier.await();
+                } catch (InterruptedException | BrokenBarrierException e) {
+                    Log.e(TAG, "Unexpected exception while waiting on CyclicBarrier", e);
+                }
+            }
+        };
+        testApp1Binder.linkToDeath(sameDeathRecipient, 0);
+        mLinkedDeathRecipients.add(Pair.create(testApp1Binder, sameDeathRecipient));
+
+        testApp2Binder.linkToDeath(sameDeathRecipient, 0);
+        mLinkedDeathRecipients.add(Pair.create(testApp2Binder, sameDeathRecipient));
+
+        mActivityManager.forceStopPackage(TEST_PACKAGE_NAME_1);
+        try {
+            barrier.await(10, TimeUnit.SECONDS);
+        } catch (TimeoutException e) {
+            fail("Timed out while waiting for 1st death notification: " + e.getMessage());
+        }
+        assertEquals("Different binder received", testApp1Binder, binderThatDied.get());
+
+        barrier.reset();
+        mActivityManager.forceStopPackage(TEST_PACKAGE_NAME_2);
+        try {
+            barrier.await(10, TimeUnit.SECONDS);
+        } catch (TimeoutException e) {
+            fail("Timed out while waiting for 2nd death notification: " + e.getMessage());
+        }
+        assertEquals("Different binder received", testApp2Binder, binderThatDied.get());
+    }
+
+    @After
+    public void tearDown() {
+        for (Pair<IBinder, IBinder.DeathRecipient> linkedPair : mLinkedDeathRecipients) {
+            linkedPair.first.unlinkToDeath(linkedPair.second, 0);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 3586216a..93f4b51 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -124,6 +124,10 @@
 
     public void setSoftKeyboardCallbackEnabled(boolean enabled) {}
 
+    public boolean switchToInputMethod(String imeId) {
+        return false;
+    }
+
     public boolean isAccessibilityButtonAvailable() {
         return false;
     }
diff --git a/core/tests/overlaytests/remount/TEST_MAPPING b/core/tests/overlaytests/remount/TEST_MAPPING
new file mode 100644
index 0000000..54dd431
--- /dev/null
+++ b/core/tests/overlaytests/remount/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name" : "OverlayRemountedTest"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/tests/overlaytests/remount/host/Android.bp b/core/tests/overlaytests/remount/host/Android.bp
new file mode 100644
index 0000000..3825c55
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_test_host {
+    name: "OverlayRemountedTest",
+    srcs: ["src/**/*.java"],
+    libs: [
+        "tradefed",
+        "junit",
+    ],
+    test_suites: ["general-tests"],
+    java_resources: [
+        ":OverlayRemountedTest_SharedLibrary",
+        ":OverlayRemountedTest_SharedLibraryOverlay",
+        ":OverlayRemountedTest_Target",
+    ],
+}
diff --git a/core/tests/overlaytests/remount/host/AndroidTest.xml b/core/tests/overlaytests/remount/host/AndroidTest.xml
new file mode 100644
index 0000000..11eadf1a
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+
+<configuration description="Test module config for OverlayRemountedTest">
+    <option name="test-tag" value="OverlayRemountedTest" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="remount" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.HostTest">
+        <option name="jar" value="OverlayRemountedTest.jar" />
+    </test>
+</configuration>
diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java
new file mode 100644
index 0000000..84af187
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.overlaytest.remounted;
+
+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 com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TemporaryFolder;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class OverlayHostTest extends BaseHostJUnit4Test {
+    private static final long TIME_OUT_MS = 30000;
+    private static final String RES_INSTRUMENTATION_ARG = "res";
+    private static final String OVERLAY_INSTRUMENTATION_ARG = "overlays";
+    private static final String RESOURCES_TYPE_SUFFIX = "_type";
+    private static final String RESOURCES_DATA_SUFFIX = "_data";
+
+    public final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+    public final SystemPreparer mPreparer = new SystemPreparer(mTemporaryFolder, this::getDevice);
+
+    @Rule
+    public final RuleChain ruleChain = RuleChain.outerRule(mTemporaryFolder).around(mPreparer);
+    private Map<String, String> mLastResults;
+
+    /**
+     * Retrieves the values of the resources in the test package. The test package must use the
+     * {@link com.android.overlaytest.remounted.target.ResourceRetrievalRunner} instrumentation.
+     **/
+    void retrieveResource(String testPackageName, List<String> requiredOverlayPaths,
+            String... resourceNames) throws DeviceNotAvailableException {
+        final HashMap<String, String> args = new HashMap<>();
+        if (!requiredOverlayPaths.isEmpty()) {
+            // Enclose the require overlay paths in quotes so the arguments will be string arguments
+            // rather than file arguments.
+            args.put(OVERLAY_INSTRUMENTATION_ARG,
+                    String.format("\"%s\"", String.join(" ", requiredOverlayPaths)));
+        }
+
+        if (resourceNames.length == 0) {
+            throw new IllegalArgumentException("Must specify at least one resource to retrieve.");
+        }
+
+        // Pass the names of the resources to retrieve into the test as one string.
+        args.put(RES_INSTRUMENTATION_ARG,
+                String.format("\"%s\"", String.join(" ", resourceNames)));
+
+        runDeviceTests(getDevice(), null, testPackageName, null, null, null, TIME_OUT_MS,
+                TIME_OUT_MS, TIME_OUT_MS, false, false, args);
+
+        // Retrieve the results of the most recently run test.
+        mLastResults = (getLastDeviceRunResults().getRunMetrics() == mLastResults) ? null :
+                getLastDeviceRunResults().getRunMetrics();
+    }
+
+    /** Returns the base resource directories of the specified packages. */
+    List<String> getPackagePaths(String... packageNames)
+            throws DeviceNotAvailableException {
+        final ArrayList<String> paths = new ArrayList<>();
+        for (String packageName : packageNames) {
+            // Use the package manager shell command to find the path of the package.
+            final String result = getDevice().executeShellCommand(
+                    String.format("pm dump %s | grep \"resourcePath=\"", packageName));
+            assertNotNull("Failed to find path for package " + packageName, result);
+            int splitIndex = result.indexOf('=');
+            assertTrue(splitIndex >= 0);
+            paths.add(result.substring(splitIndex + 1).trim());
+        }
+        return paths;
+    }
+
+    /** Builds the full name of a resource in the form package:type/entry. */
+    String resourceName(String pkg, String type, String entry) {
+        return String.format("%s:%s/%s", pkg, type, entry);
+    }
+
+    /**
+     * Asserts that the type and data of a a previously retrieved is the same as expected.
+     * @param resourceName the full name of the resource in the form package:type/entry
+     * @param type the expected {@link android.util.TypedValue} type of the resource
+     * @param data the expected value of the resource when coerced to a string using
+     *             {@link android.util.TypedValue#coerceToString()}
+     **/
+    void assertResource(String resourceName, int type, String data) {
+        assertNotNull("Failed to get test results", mLastResults);
+        assertNotEquals("No resource values were retrieved", mLastResults.size(), 0);
+        assertEquals("" + type, mLastResults.get(resourceName + RESOURCES_TYPE_SUFFIX));
+        assertEquals("" + data, mLastResults.get(resourceName + RESOURCES_DATA_SUFFIX));
+    }
+}
diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java
new file mode 100644
index 0000000..4939e16
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.overlaytest.remounted;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class OverlaySharedLibraryTest extends OverlayHostTest {
+    private static final String TARGET_APK = "OverlayRemountedTest_Target.apk";
+    private static final String TARGET_PACKAGE = "com.android.overlaytest.remounted.target";
+    private static final String SHARED_LIBRARY_APK =
+            "OverlayRemountedTest_SharedLibrary.apk";
+    private static final String SHARED_LIBRARY_PACKAGE =
+            "com.android.overlaytest.remounted.shared_library";
+    private static final String SHARED_LIBRARY_OVERLAY_APK =
+            "OverlayRemountedTest_SharedLibraryOverlay.apk";
+    private static final String SHARED_LIBRARY_OVERLAY_PACKAGE =
+            "com.android.overlaytest.remounted.shared_library.overlay";
+
+    @Test
+    public void testSharedLibrary() throws Exception {
+        final String targetResource = resourceName(TARGET_PACKAGE, "bool",
+                "uses_shared_library_overlaid");
+        final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool",
+                "shared_library_overlaid");
+
+        mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
+                .installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE)
+                .reboot()
+                .setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, false)
+                .installResourceApk(TARGET_APK, TARGET_PACKAGE);
+
+        // The shared library resource is not currently overlaid.
+        retrieveResource(Collections.emptyList(), targetResource, libraryResource);
+        assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "false");
+        assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "false");
+
+        // Overlay the shared library resource.
+        mPreparer.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true);
+        retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource,
+                libraryResource);
+        assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
+        assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
+    }
+
+    @Test
+    public void testSharedLibraryPreEnabled() throws Exception {
+        final String targetResource = resourceName(TARGET_PACKAGE, "bool",
+                "uses_shared_library_overlaid");
+        final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool",
+                "shared_library_overlaid");
+
+        mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
+                .installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE)
+                .setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true)
+                .reboot()
+                .installResourceApk(TARGET_APK, TARGET_PACKAGE);
+
+        retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource,
+                libraryResource);
+        assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
+        assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
+    }
+
+    private void retrieveResource(List<String> requiredOverlayPaths, String... resourceNames)
+            throws DeviceNotAvailableException {
+        retrieveResource(TARGET_PACKAGE, requiredOverlayPaths, resourceNames);
+    }
+}
diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java
new file mode 100644
index 0000000..7028f2f
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java
@@ -0,0 +1,158 @@
+/*
+ * 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.overlaytest.remounted;
+
+import static org.junit.Assert.assertTrue;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+import org.junit.Assert;
+import org.junit.rules.ExternalResource;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeoutException;
+
+class SystemPreparer extends ExternalResource {
+    private static final long REBOOT_SLEEP_MS = 30000;
+    private static final long OVERLAY_ENABLE_TIMEOUT_MS = 20000;
+
+    // The paths of the files pushed onto the device through this rule.
+    private ArrayList<String> mPushedFiles = new ArrayList<>();
+
+    // The package names of packages installed through this rule.
+    private ArrayList<String> mInstalledPackages = new ArrayList<>();
+
+    private final TemporaryFolder mHostTempFolder;
+    private final DeviceProvider mDeviceProvider;
+
+    SystemPreparer(TemporaryFolder hostTempFolder, DeviceProvider deviceProvider) {
+        mHostTempFolder = hostTempFolder;
+        mDeviceProvider = deviceProvider;
+    }
+
+    /** Copies a file within the host test jar to a path on device. */
+    SystemPreparer pushResourceFile(String resourcePath,
+            String outputPath) throws DeviceNotAvailableException, IOException {
+        final ITestDevice device = mDeviceProvider.getDevice();
+        assertTrue(device.pushFile(copyResourceToTemp(resourcePath), outputPath));
+        mPushedFiles.add(outputPath);
+        return this;
+    }
+
+    /** Installs an APK within the host test jar onto the device. */
+    SystemPreparer installResourceApk(String resourcePath, String packageName)
+            throws DeviceNotAvailableException, IOException {
+        final ITestDevice device = mDeviceProvider.getDevice();
+        final File tmpFile = copyResourceToTemp(resourcePath);
+        final String result = device.installPackage(tmpFile, true);
+        Assert.assertNull(result);
+        mInstalledPackages.add(packageName);
+        return this;
+    }
+
+    /** Sets the enable state of an overlay pacakage. */
+    SystemPreparer setOverlayEnabled(String packageName, boolean enabled)
+            throws ExecutionException, TimeoutException {
+        final ITestDevice device = mDeviceProvider.getDevice();
+
+        // Wait for the overlay to change its enabled state.
+        final FutureTask<Boolean> enabledListener = new FutureTask<>(() -> {
+            while (true) {
+                device.executeShellCommand(String.format("cmd overlay %s %s",
+                        enabled ? "enable" : "disable", packageName));
+
+                final String pattern = (enabled ? "[x]" : "[ ]") + " " + packageName;
+                if (device.executeShellCommand("cmd overlay list").contains(pattern)) {
+                    return true;
+                }
+            }
+        });
+
+        final Executor executor = (cmd) -> new Thread(cmd).start();
+        executor.execute(enabledListener);
+        try {
+            enabledListener.get(OVERLAY_ENABLE_TIMEOUT_MS, MILLISECONDS);
+        } catch (InterruptedException ignored) {
+        }
+
+        return this;
+    }
+
+    /** Restarts the device and waits until after boot is completed. */
+    SystemPreparer reboot() throws DeviceNotAvailableException {
+        final ITestDevice device = mDeviceProvider.getDevice();
+        device.executeShellCommand("stop");
+        device.executeShellCommand("start");
+        try {
+            // Sleep until the device is ready for test execution.
+            Thread.sleep(REBOOT_SLEEP_MS);
+        } catch (InterruptedException ignored) {
+        }
+
+        return this;
+    }
+
+    /** Copies a file within the host test jar to a temporary file on the host machine. */
+    private File copyResourceToTemp(String resourcePath) throws IOException {
+        final File tempFile = mHostTempFolder.newFile(resourcePath);
+        final ClassLoader classLoader = getClass().getClassLoader();
+        try (InputStream assetIs = classLoader.getResource(resourcePath).openStream();
+             FileOutputStream assetOs = new FileOutputStream(tempFile)) {
+            if (assetIs == null) {
+                throw new IllegalStateException("Failed to find resource " + resourcePath);
+            }
+
+            int b;
+            while ((b = assetIs.read()) >= 0) {
+                assetOs.write(b);
+            }
+        }
+
+        return tempFile;
+    }
+
+    /** Removes installed packages and files that were pushed to the device. */
+    @Override
+    protected void after() {
+        final ITestDevice device = mDeviceProvider.getDevice();
+        try {
+            for (final String file : mPushedFiles) {
+                device.deleteFile(file);
+            }
+            for (final String packageName : mInstalledPackages) {
+                device.uninstallPackage(packageName);
+            }
+        } catch (DeviceNotAvailableException e) {
+            Assert.fail(e.toString());
+        }
+    }
+
+    interface DeviceProvider {
+        ITestDevice getDevice();
+    }
+}
diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/Android.bp b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/Android.bp
new file mode 100644
index 0000000..ffb0572
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/Android.bp
@@ -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.
+
+android_test_helper_app {
+    name: "OverlayRemountedTest_SharedLibrary",
+    sdk_version: "current",
+    aaptflags: ["--shared-lib"],
+}
diff --git a/core/res/res/interpolator/screen_rotation_alpha_in.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/AndroidManifest.xml
similarity index 71%
copy from core/res/res/interpolator/screen_rotation_alpha_in.xml
copy to core/tests/overlaytests/remount/host/test-apps/SharedLibrary/AndroidManifest.xml
index 9c566a7..06e3f6a 100644
--- a/core/res/res/interpolator/screen_rotation_alpha_in.xml
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/AndroidManifest.xml
@@ -15,8 +15,9 @@
   ~ 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"/>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.overlaytest.remounted.shared_library">
+    <application>
+        <library android:name="com.android.overlaytest.remounted.shared_library" />
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/core/res/res/interpolator/screen_rotation_alpha_in.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/overlayable.xml
similarity index 76%
copy from core/res/res/interpolator/screen_rotation_alpha_in.xml
copy to core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/overlayable.xml
index 9c566a7..1b06f6d 100644
--- a/core/res/res/interpolator/screen_rotation_alpha_in.xml
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/overlayable.xml
@@ -15,8 +15,10 @@
   ~ 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"/>
+<resources>
+    <overlayable name="TestResources">
+        <policy type="public">
+            <item type="bool" name="shared_library_overlaid" />
+        </policy>
+    </overlayable>
+</resources>
diff --git a/core/res/res/interpolator/screen_rotation_alpha_in.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/public.xml
similarity index 77%
copy from core/res/res/interpolator/screen_rotation_alpha_in.xml
copy to core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/public.xml
index 9c566a7..5b9db16 100644
--- a/core/res/res/interpolator/screen_rotation_alpha_in.xml
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/public.xml
@@ -15,8 +15,6 @@
   ~ 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"/>
+<resources>
+    <public type="bool" name="shared_library_overlaid" id="0x00050001"/>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/interpolator/screen_rotation_alpha_in.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/values.xml
similarity index 77%
rename from core/res/res/interpolator/screen_rotation_alpha_in.xml
rename to core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/values.xml
index 9c566a7..2dc47a7 100644
--- a/core/res/res/interpolator/screen_rotation_alpha_in.xml
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/values.xml
@@ -15,8 +15,6 @@
   ~ 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"/>
+<resources>
+    <bool name="shared_library_overlaid">false</bool>
+</resources>
diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/Android.bp b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/Android.bp
new file mode 100644
index 0000000..0d29aec
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/Android.bp
@@ -0,0 +1,18 @@
+// 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.
+
+android_test_helper_app {
+    name: "OverlayRemountedTest_SharedLibraryOverlay",
+    sdk_version: "current",
+}
diff --git a/core/res/res/interpolator/screen_rotation_alpha_in.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/AndroidManifest.xml
similarity index 66%
copy from core/res/res/interpolator/screen_rotation_alpha_in.xml
copy to core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/AndroidManifest.xml
index 9c566a7..53a4e61 100644
--- a/core/res/res/interpolator/screen_rotation_alpha_in.xml
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/AndroidManifest.xml
@@ -15,8 +15,9 @@
   ~ 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"/>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.overlaytest.remounted.shared_library.overlay">
+    <application android:hasCode="false" />
+    <overlay android:targetPackage="com.android.overlaytest.remounted.shared_library"
+             android:targetName="TestResources" />
+</manifest>
\ No newline at end of file
diff --git a/core/res/res/interpolator/screen_rotation_alpha_in.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/res/values/values.xml
similarity index 77%
copy from core/res/res/interpolator/screen_rotation_alpha_in.xml
copy to core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/res/values/values.xml
index 9c566a7..f66448a 100644
--- a/core/res/res/interpolator/screen_rotation_alpha_in.xml
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/res/values/values.xml
@@ -15,8 +15,6 @@
   ~ 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"/>
+<resources>
+    <bool name="shared_library_overlaid">true</bool>
+</resources>
diff --git a/core/tests/overlaytests/remount/target/Android.bp b/core/tests/overlaytests/remount/target/Android.bp
new file mode 100644
index 0000000..83f9f28
--- /dev/null
+++ b/core/tests/overlaytests/remount/target/Android.bp
@@ -0,0 +1,20 @@
+// Copyright (C) 2018 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.
+
+android_test_helper_app {
+    name: "OverlayRemountedTest_Target",
+    srcs: ["src/**/*.java"],
+    sdk_version: "test_current",
+    libs: ["OverlayRemountedTest_SharedLibrary"],
+}
diff --git a/core/tests/overlaytests/remount/target/AndroidManifest.xml b/core/tests/overlaytests/remount/target/AndroidManifest.xml
new file mode 100644
index 0000000..32fec43
--- /dev/null
+++ b/core/tests/overlaytests/remount/target/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.overlaytest.remounted.target">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="com.android.overlaytest.remounted.shared_library"
+                      android:required="true" />
+    </application>
+
+    <instrumentation android:name="com.android.overlaytest.remounted.target.ResourceRetrievalRunner"
+                     android:targetPackage="com.android.overlaytest.remounted.target"
+                     android:label="Remounted system RRO tests" />
+</manifest>
diff --git a/core/res/res/interpolator/screen_rotation_alpha_in.xml b/core/tests/overlaytests/remount/target/res/values/values.xml
similarity index 74%
copy from core/res/res/interpolator/screen_rotation_alpha_in.xml
copy to core/tests/overlaytests/remount/target/res/values/values.xml
index 9c566a7..b5f444a 100644
--- a/core/res/res/interpolator/screen_rotation_alpha_in.xml
+++ b/core/tests/overlaytests/remount/target/res/values/values.xml
@@ -15,8 +15,6 @@
   ~ 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"/>
+<resources xmlns:sharedlib="http://schemas.android.com/apk/res/com.android.overlaytest.remounted.shared_library">
+    <bool name="uses_shared_library_overlaid">@sharedlib:bool/shared_library_overlaid</bool>
+</resources>
diff --git a/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java b/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java
new file mode 100644
index 0000000..2e4c211
--- /dev/null
+++ b/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java
@@ -0,0 +1,140 @@
+/*
+ * 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.overlaytest.remounted.target;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.TypedValue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An {@link Instrumentation} that retrieves the value of specified resources within the
+ * application.
+ **/
+public class ResourceRetrievalRunner extends Instrumentation {
+    private static final String TAG = ResourceRetrievalRunner.class.getSimpleName();
+
+    // A list of whitespace separated resource names of which to retrieve the resource values.
+    private static final String RESOURCE_LIST_TAG = "res";
+
+    // A list of whitespace separated overlay package paths that must be present before retrieving
+    // resource values.
+    private static final String REQUIRED_OVERLAYS_LIST_TAG = "overlays";
+
+    // The suffixes of the keys returned from the instrumentation. To retrieve the type of a
+    // resource looked up with the instrumentation, append the {@link #RESOURCES_TYPE_SUFFIX} suffix
+    // to the end of the name of the resource. For the value of a resource, use
+    // {@link #RESOURCES_DATA_SUFFIX} instead.
+    private static final String RESOURCES_TYPE_SUFFIX = "_type";
+    private static final String RESOURCES_DATA_SUFFIX = "_data";
+
+    // The amount of time in seconds to wait for the overlays to be present in the AssetManager.
+    private static final int OVERLAY_PATH_TIMEOUT = 60;
+
+    private final ArrayList<String> mResourceNames = new ArrayList<>();
+    private final ArrayList<String> mOverlayPaths = new ArrayList<>();
+    private final Bundle mResult = new Bundle();
+
+    /**
+     * Receives the instrumentation arguments and runs the resource retrieval.
+     * The entry with key {@link #RESOURCE_LIST_TAG} in the {@link Bundle} arguments is a
+     * whitespace separated string of resource names of which to retrieve the resource values.
+     * The entry with key {@link #REQUIRED_OVERLAYS_LIST_TAG} in the {@link Bundle} arguments is a
+     * whitespace separated string of overlay package paths prefixes that must be present before
+     * retrieving the resource values.
+     */
+    @Override
+    public void onCreate(Bundle arguments) {
+        super.onCreate(arguments);
+        mResourceNames.addAll(Arrays.asList(arguments.getString(RESOURCE_LIST_TAG).split(" ")));
+        if (arguments.containsKey(REQUIRED_OVERLAYS_LIST_TAG)) {
+            mOverlayPaths.addAll(Arrays.asList(
+                    arguments.getString(REQUIRED_OVERLAYS_LIST_TAG).split(" ")));
+        }
+        start();
+    }
+
+    @Override
+    public void onStart() {
+        final Resources res = getContext().getResources();
+        res.getAssets().setResourceResolutionLoggingEnabled(true);
+
+        if (!mOverlayPaths.isEmpty()) {
+            Log.d(TAG, String.format("Waiting for overlay paths [%s]",
+                    String.join(",", mOverlayPaths)));
+
+            // Wait for all required overlays to be present in the AssetManager.
+            final FutureTask<Boolean> overlayListener = new FutureTask<>(() -> {
+                while (!mOverlayPaths.isEmpty()) {
+                    final String[] apkPaths = res.getAssets().getApkPaths();
+                    for (String path : apkPaths) {
+                        for (String overlayPath : mOverlayPaths) {
+                            if (path.startsWith(overlayPath)) {
+                                mOverlayPaths.remove(overlayPath);
+                                break;
+                            }
+                        }
+                    }
+                }
+                return true;
+            });
+
+            try {
+                final Executor executor = (t) -> new Thread(t).start();
+                executor.execute(overlayListener);
+                overlayListener.get(OVERLAY_PATH_TIMEOUT, TimeUnit.SECONDS);
+            } catch (Exception e) {
+                Log.e(TAG, String.format("Failed to wait for required overlays [%s]",
+                        String.join(",", mOverlayPaths)), e);
+                finish(Activity.RESULT_CANCELED, mResult);
+            }
+        }
+
+        // Retrieve the values for each resource passed in.
+        final TypedValue typedValue = new TypedValue();
+        for (final String resourceName : mResourceNames) {
+            try {
+                final int resId = res.getIdentifier(resourceName, null, null);
+                res.getValue(resId, typedValue, true);
+                Log.d(TAG, String.format("Resolution for 0x%s: %s", Integer.toHexString(resId),
+                        res.getAssets().getLastResourceResolution()));
+            } catch (Resources.NotFoundException e) {
+                Log.e(TAG, "Failed to retrieve value for resource " + resourceName, e);
+                finish(Activity.RESULT_CANCELED, mResult);
+            }
+
+            putValue(resourceName, typedValue);
+        }
+
+        finish(Activity.RESULT_OK, mResult);
+    }
+
+    private void putValue(String resourceName, TypedValue value) {
+        mResult.putInt(resourceName + RESOURCES_TYPE_SUFFIX, value.type);
+        final CharSequence textValue = value.coerceToString();
+        mResult.putString(resourceName + RESOURCES_DATA_SUFFIX,
+                textValue == null ? "null" : textValue.toString());
+    }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java b/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java
new file mode 100644
index 0000000..0809f69
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.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.internal.util;
+
+import static org.junit.Assert.assertTrue;
+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.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+
+/** Unit tests for {@link ConnectivityUtil}. */
+public class ConnectivityUtilTest {
+
+    public static final String TAG = "ConnectivityUtilTest";
+
+    // Mock objects for testing
+    @Mock private Context mMockContext;
+    @Mock private PackageManager mMockPkgMgr;
+    @Mock private ApplicationInfo mMockApplInfo;
+    @Mock private AppOpsManager mMockAppOps;
+    @Mock private UserManager mMockUserManager;
+    @Mock private LocationManager mLocationManager;
+
+    private static final String TEST_PKG_NAME = "com.google.somePackage";
+    private static final String TEST_FEATURE_ID = "com.google.someFeature";
+    private static final int MANAGED_PROFILE_UID = 1100000;
+    private static final int OTHER_USER_UID = 1200000;
+
+    private final String mInteractAcrossUsersFullPermission =
+            "android.permission.INTERACT_ACROSS_USERS_FULL";
+    private final String mManifestStringCoarse =
+            Manifest.permission.ACCESS_COARSE_LOCATION;
+    private final String mManifestStringFine =
+            Manifest.permission.ACCESS_FINE_LOCATION;
+
+    // Test variables
+    private int mWifiScanAllowApps;
+    private int mUid;
+    private int mCoarseLocationPermission;
+    private int mAllowCoarseLocationApps;
+    private int mFineLocationPermission;
+    private int mAllowFineLocationApps;
+    private int mCurrentUser;
+    private boolean mIsLocationEnabled;
+    private boolean mThrowSecurityException;
+    private Answer<Integer> mReturnPermission;
+    private HashMap<String, Integer> mPermissionsList = new HashMap<String, Integer>();
+
+    private class TestConnectivityUtil extends ConnectivityUtil {
+
+        TestConnectivityUtil(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected int getCurrentUser() {
+            return mCurrentUser;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        initTestVars();
+    }
+
+    private void setupMocks() throws Exception {
+        when(mMockPkgMgr.getApplicationInfoAsUser(eq(TEST_PKG_NAME), eq(0), any()))
+                .thenReturn(mMockApplInfo);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPkgMgr);
+        when(mMockAppOps.noteOp(AppOpsManager.OPSTR_WIFI_SCAN, mUid, TEST_PKG_NAME,
+                TEST_FEATURE_ID, null)).thenReturn(mWifiScanAllowApps);
+        when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_COARSE_LOCATION), eq(mUid),
+                eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
+                .thenReturn(mAllowCoarseLocationApps);
+        when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_FINE_LOCATION), eq(mUid),
+                eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
+                .thenReturn(mAllowFineLocationApps);
+        if (mThrowSecurityException) {
+            doThrow(new SecurityException("Package " + TEST_PKG_NAME + " doesn't belong"
+                    + " to application bound to user " + mUid))
+                    .when(mMockAppOps).checkPackage(mUid, TEST_PKG_NAME);
+        }
+        when(mMockContext.getSystemService(Context.APP_OPS_SERVICE))
+                .thenReturn(mMockAppOps);
+        when(mMockContext.getSystemService(Context.USER_SERVICE))
+                .thenReturn(mMockUserManager);
+        when(mMockContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
+    }
+
+    private void setupTestCase() throws Exception {
+        setupMocks();
+        setupMockInterface();
+    }
+
+    private void initTestVars() {
+        mPermissionsList.clear();
+        mReturnPermission = createPermissionAnswer();
+        mWifiScanAllowApps = AppOpsManager.MODE_ERRORED;
+        mUid = OTHER_USER_UID;
+        mThrowSecurityException = true;
+        mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.M;
+        mIsLocationEnabled = false;
+        mCurrentUser = UserHandle.USER_SYSTEM;
+        mCoarseLocationPermission = PackageManager.PERMISSION_DENIED;
+        mFineLocationPermission = PackageManager.PERMISSION_DENIED;
+        mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED;
+        mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
+    }
+
+    private void setupMockInterface() {
+        Binder.restoreCallingIdentity((((long) mUid) << 32) | Binder.getCallingPid());
+        doAnswer(mReturnPermission).when(mMockContext).checkPermission(
+                anyString(), anyInt(), anyInt());
+        when(mMockUserManager.isSameProfileGroup(UserHandle.SYSTEM,
+                UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID)))
+                .thenReturn(true);
+        when(mMockContext.checkPermission(mManifestStringCoarse, -1, mUid))
+                .thenReturn(mCoarseLocationPermission);
+        when(mMockContext.checkPermission(mManifestStringFine, -1, mUid))
+                .thenReturn(mFineLocationPermission);
+        when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(mIsLocationEnabled);
+    }
+
+    private Answer<Integer> createPermissionAnswer() {
+        return new Answer<Integer>() {
+            @Override
+            public Integer answer(InvocationOnMock invocation) {
+                int myUid = (int) invocation.getArguments()[1];
+                String myPermission = (String) invocation.getArguments()[0];
+                mPermissionsList.get(myPermission);
+                if (mPermissionsList.containsKey(myPermission)) {
+                    int uid = mPermissionsList.get(myPermission);
+                    if (myUid == uid) {
+                        return PackageManager.PERMISSION_GRANTED;
+                    }
+                }
+                return PackageManager.PERMISSION_DENIED;
+            }
+        };
+    }
+
+    @Test
+    public void testEnforceLocationPermission_HasAllPermissions_BeforeQ() throws Exception {
+        mIsLocationEnabled = true;
+        mThrowSecurityException = false;
+        mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
+        mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED;
+        mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+        mUid = mCurrentUser;
+        setupTestCase();
+        new TestConnectivityUtil(mMockContext)
+                .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
+    }
+
+    @Test
+    public void testEnforceLocationPermission_HasAllPermissions_AfterQ() throws Exception {
+        mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
+        mIsLocationEnabled = true;
+        mThrowSecurityException = false;
+        mUid = mCurrentUser;
+        mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
+        mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
+        mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+        setupTestCase();
+        new TestConnectivityUtil(mMockContext)
+                .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
+    }
+
+    @Test
+    public void testEnforceLocationPermission_PkgNameAndUidMismatch() throws Exception {
+        mThrowSecurityException = true;
+        mIsLocationEnabled = true;
+        mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
+        mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
+        mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+        setupTestCase();
+
+        assertThrows(SecurityException.class,
+                () -> new TestConnectivityUtil(mMockContext)
+                        .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+    }
+
+    @Test
+    public void testenforceCanAccessScanResults_UserOrProfileNotCurrent() throws Exception {
+        mIsLocationEnabled = true;
+        mThrowSecurityException = false;
+        mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
+        mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED;
+        mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+        setupTestCase();
+
+        assertThrows(SecurityException.class,
+                () -> new TestConnectivityUtil(mMockContext)
+                        .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+    }
+
+    @Test
+    public void testenforceCanAccessScanResults_NoCoarseLocationPermission() throws Exception {
+        mThrowSecurityException = false;
+        mIsLocationEnabled = true;
+        setupTestCase();
+        assertThrows(SecurityException.class,
+                () -> new TestConnectivityUtil(mMockContext)
+                        .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+    }
+
+    @Test
+    public void testenforceCanAccessScanResults_NoFineLocationPermission() throws Exception {
+        mThrowSecurityException = false;
+        mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
+        mIsLocationEnabled = true;
+        mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
+        mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
+        mUid = MANAGED_PROFILE_UID;
+        setupTestCase();
+
+        assertThrows(SecurityException.class,
+                () -> new TestConnectivityUtil(mMockContext)
+                        .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+        verify(mMockAppOps, never()).noteOp(anyInt(), anyInt(), anyString());
+    }
+
+    @Test
+    public void testenforceCanAccessScanResults_LocationModeDisabled() throws Exception {
+        mThrowSecurityException = false;
+        mUid = MANAGED_PROFILE_UID;
+        mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+        mPermissionsList.put(mInteractAcrossUsersFullPermission, mUid);
+        mIsLocationEnabled = false;
+
+        setupTestCase();
+
+        assertThrows(SecurityException.class,
+                () -> new TestConnectivityUtil(mMockContext)
+                        .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+    }
+
+    private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
+        try {
+            r.run();
+            Assert.fail("Expected " + exceptionClass + " to be thrown.");
+        } catch (Exception exception) {
+            assertTrue(exceptionClass.isInstance(exception));
+        }
+    }
+}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 8765719..3f2f349 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -44,6 +44,7 @@
         "AttributeResolution.cpp",
         "ChunkIterator.cpp",
         "ConfigDescription.cpp",
+        "DynamicLibManager.cpp",
         "Idmap.cpp",
         "LoadedArsc.cpp",
         "Locale.cpp",
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index ca4143f..8cfd2d8 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -25,6 +25,7 @@
 
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
+#include "androidfw/DynamicLibManager.h"
 #include "androidfw/ResourceUtils.h"
 #include "androidfw/Util.h"
 #include "utils/ByteOrder.h"
@@ -66,7 +67,12 @@
   StringPoolRef entry_string_ref;
 };
 
-AssetManager2::AssetManager2() {
+AssetManager2::AssetManager2() : dynamic_lib_manager_(std::make_unique<DynamicLibManager>()) {
+  memset(&configuration_, 0, sizeof(configuration_));
+}
+
+AssetManager2::AssetManager2(DynamicLibManager* dynamic_lib_manager)
+    : dynamic_lib_manager_(dynamic_lib_manager) {
   memset(&configuration_, 0, sizeof(configuration_));
 }
 
@@ -85,25 +91,45 @@
   package_groups_.clear();
   package_ids_.fill(0xff);
 
-  // A mapping from apk assets path to the runtime package id of its first loaded package.
+  // Overlay resources are not directly referenced by an application so their resource ids
+  // can change throughout the application's lifetime. Assign overlay package ids last.
+  std::vector<const ApkAssets*> sorted_apk_assets(apk_assets_);
+  std::stable_partition(sorted_apk_assets.begin(), sorted_apk_assets.end(), [](const ApkAssets* a) {
+    return !a->IsOverlay();
+  });
+
   std::unordered_map<std::string, uint8_t> apk_assets_package_ids;
+  std::unordered_map<std::string, uint8_t> package_name_package_ids;
 
-  // 0x01 is reserved for the android package.
-  int next_package_id = 0x02;
-  const size_t apk_assets_count = apk_assets_.size();
-  for (size_t i = 0; i < apk_assets_count; i++) {
-    const ApkAssets* apk_assets = apk_assets_[i];
-    const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
-
-    for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) {
-      // Get the package ID or assign one if a shared library.
-      int package_id;
-      if (package->IsDynamic()) {
-        package_id = next_package_id++;
-      } else {
-        package_id = package->GetPackageId();
+  // Assign stable package ids to application packages.
+  uint8_t next_available_package_id = 0U;
+  for (const auto& apk_assets : sorted_apk_assets) {
+    for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) {
+      uint8_t package_id = package->GetPackageId();
+      if (package->IsOverlay()) {
+        package_id = GetDynamicLibManager()->FindUnassignedId(next_available_package_id);
+        next_available_package_id = package_id + 1;
+      } else if (package->IsDynamic()) {
+        package_id = GetDynamicLibManager()->GetAssignedId(package->GetPackageName());
       }
 
+      // Map the path of the apk assets to the package id of its first loaded package.
+      apk_assets_package_ids[apk_assets->GetPath()] = package_id;
+
+      // Map the package name of the package to the first loaded package with that package id.
+      package_name_package_ids[package->GetPackageName()] = package_id;
+    }
+  }
+
+  const int apk_assets_count = apk_assets_.size();
+  for (int i = 0; i < apk_assets_count; i++) {
+    const auto& apk_assets = apk_assets_[i];
+    for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) {
+      const auto package_id_entry = package_name_package_ids.find(package->GetPackageName());
+      CHECK(package_id_entry != package_name_package_ids.end())
+          << "no package id assgined to package " << package->GetPackageName();
+      const uint8_t package_id = package_id_entry->second;
+
       // Add the mapping for package ID to index if not present.
       uint8_t idx = package_ids_[package_id];
       if (idx == 0xff) {
@@ -115,7 +141,10 @@
           // to take effect.
           const auto& loaded_idmap = apk_assets->GetLoadedIdmap();
           auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath());
-          if (target_package_iter != apk_assets_package_ids.end()) {
+          if (target_package_iter == apk_assets_package_ids.end()) {
+             LOG(INFO) << "failed to find target package for overlay "
+                       << loaded_idmap->OverlayApkPath();
+          } else {
             const uint8_t target_package_id = target_package_iter->second;
             const uint8_t target_idx = package_ids_[target_package_id];
             CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not"
@@ -123,7 +152,7 @@
 
             PackageGroup& target_package_group = package_groups_[target_idx];
 
-            // Create a special dynamic reference table for the overlay to rewite references to
+            // Create a special dynamic reference table for the overlay to rewrite references to
             // overlay resources as references to the target resources they overlay.
             auto overlay_table = std::make_shared<OverlayDynamicRefTable>(
                 loaded_idmap->GetOverlayDynamicRefTable(target_package_id));
@@ -153,8 +182,6 @@
         package_group->dynamic_ref_table->mEntries.replaceValueFor(
             package_name, static_cast<uint8_t>(entry.package_id));
       }
-
-      apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id));
     }
   }
 
@@ -567,7 +594,7 @@
       if (resource_resolution_logging_enabled_) {
         last_resolution_.steps.push_back(
             Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(),
-                             &package_group.packages_[0].loaded_package_->GetPackageName()});
+                             overlay_result.package_name});
       }
     }
   }
@@ -1279,6 +1306,16 @@
   return 0;
 }
 
+DynamicLibManager* AssetManager2::GetDynamicLibManager() const {
+  auto dynamic_lib_manager =
+      std::get_if<std::unique_ptr<DynamicLibManager>>(&dynamic_lib_manager_);
+  if (dynamic_lib_manager) {
+    return (*dynamic_lib_manager).get();
+  } else {
+    return *std::get_if<DynamicLibManager*>(&dynamic_lib_manager_);
+  }
+}
+
 std::unique_ptr<Theme> AssetManager2::NewTheme() {
   return std::unique_ptr<Theme>(new Theme(this));
 }
diff --git a/libs/androidfw/DynamicLibManager.cpp b/libs/androidfw/DynamicLibManager.cpp
new file mode 100644
index 0000000..895b769
--- /dev/null
+++ b/libs/androidfw/DynamicLibManager.cpp
@@ -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.
+ */
+
+#include "androidfw/DynamicLibManager.h"
+
+namespace android {
+
+uint8_t DynamicLibManager::GetAssignedId(const std::string& library_package_name) {
+  auto lib_entry = shared_lib_package_ids_.find(library_package_name);
+  if (lib_entry != shared_lib_package_ids_.end()) {
+    return lib_entry->second;
+  }
+
+  return shared_lib_package_ids_[library_package_name] = next_package_id_++;
+}
+
+uint8_t DynamicLibManager::FindUnassignedId(uint8_t start_package_id) {
+  return (start_package_id < next_package_id_) ? next_package_id_ : start_package_id;
+}
+
+} // namespace android
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 00cbbca..b2cec2a 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -27,6 +27,7 @@
 #include "androidfw/ApkAssets.h"
 #include "androidfw/Asset.h"
 #include "androidfw/AssetManager.h"
+#include "androidfw/DynamicLibManager.h"
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/Util.h"
 
@@ -94,6 +95,7 @@
   };
 
   AssetManager2();
+  explicit AssetManager2(DynamicLibManager* dynamic_lib_manager);
 
   // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
   // are not owned by the AssetManager, and must have a longer lifetime.
@@ -371,6 +373,8 @@
   // Retrieve the assigned package id of the package if loaded into this AssetManager
   uint8_t GetAssignedPackageId(const LoadedPackage* package) const;
 
+  DynamicLibManager* GetDynamicLibManager() const;
+
   // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
   // have a longer lifetime.
   std::vector<const ApkAssets*> apk_assets_;
@@ -389,6 +393,9 @@
   // may need to be purged.
   ResTable_config configuration_;
 
+  // Component responsible for assigning package ids to shared libraries.
+  std::variant<std::unique_ptr<DynamicLibManager>, DynamicLibManager*> dynamic_lib_manager_;
+
   // Cached set of bags. These are cached because they can inherit keys from parent bags,
   // which involves some calculation.
   std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
diff --git a/libs/androidfw/include/androidfw/DynamicLibManager.h b/libs/androidfw/include/androidfw/DynamicLibManager.h
new file mode 100644
index 0000000..1ff7079
--- /dev/null
+++ b/libs/androidfw/include/androidfw/DynamicLibManager.h
@@ -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.
+ */
+
+#ifndef ANDROIDFW_DYNAMICLIBMANAGER_H
+#define ANDROIDFW_DYNAMICLIBMANAGER_H
+
+#include <string>
+#include <unordered_map>
+
+#include "android-base/macros.h"
+
+namespace android {
+
+// Manages assigning resource ids for dynamic resources.
+class DynamicLibManager {
+ public:
+  DynamicLibManager() = default;
+
+  // Retrieves the assigned package id for the library.
+  uint8_t GetAssignedId(const std::string& library_package_name);
+
+  // Queries in ascending order for the first available package id that is not currently assigned to
+  // a library.
+  uint8_t FindUnassignedId(uint8_t start_package_id);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DynamicLibManager);
+
+  uint8_t next_package_id_ = 0x02;
+  std::unordered_map<std::string, uint8_t> shared_lib_package_ids_;
+};
+
+} // namespace android
+
+#endif //ANDROIDFW_DYNAMICLIBMANAGER_H
diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h
index 64924f4..8891512 100644
--- a/libs/androidfw/include/androidfw/MutexGuard.h
+++ b/libs/androidfw/include/androidfw/MutexGuard.h
@@ -47,7 +47,8 @@
   static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer");
 
  public:
-  explicit Guarded() : guarded_() {
+  template <typename ...Args>
+  explicit Guarded(Args&& ...args) : guarded_(std::forward<Args>(args)...) {
   }
 
   template <typename U = T>
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index b3190be..2f6f3df 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -214,6 +214,25 @@
   EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data);
 }
 
+TEST_F(AssetManager2Test, AssignsUnchangingPackageIdToSharedLibrary) {
+  DynamicLibManager lib_manager;
+  AssetManager2 assetmanager(&lib_manager);
+  assetmanager.SetApkAssets(
+      {lib_one_assets_.get(), lib_two_assets_.get(), libclient_assets_.get()});
+
+  AssetManager2 assetmanager2(&lib_manager);
+  assetmanager2.SetApkAssets(
+      {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+
+  uint32_t res_id = assetmanager.GetResourceId("com.android.lib_one:string/foo");
+  ASSERT_NE(0U, res_id);
+
+  uint32_t res_id_2 = assetmanager2.GetResourceId("com.android.lib_one:string/foo");
+  ASSERT_NE(0U, res_id_2);
+
+  ASSERT_EQ(res_id, res_id_2);
+}
+
 TEST_F(AssetManager2Test, GetSharedLibraryResourceName) {
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({lib_one_assets_.get()});
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 2314072..12681ae 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -746,7 +746,10 @@
     glyphFunc(buffer.glyphs, buffer.pos);
 
     sk_sp<SkTextBlob> textBlob(builder.make());
-    mCanvas->drawTextBlob(textBlob, 0, 0, paintCopy);
+
+    apply_looper(&paintCopy, [&](const SkPaint& p) {
+        mCanvas->drawTextBlob(textBlob, 0, 0, p);
+    });
     drawTextDecorations(x, y, totalAdvance, paintCopy);
 }
 
@@ -783,8 +786,10 @@
         xform[i - start].fTx = pos.x() - tan.y() * y - halfWidth * tan.x();
         xform[i - start].fTy = pos.y() + tan.x() * y - halfWidth * tan.y();
     }
-
-    this->asSkCanvas()->drawTextBlob(builder.make(), 0, 0, paintCopy);
+    auto* finalCanvas = this->asSkCanvas();
+    apply_looper(&paintCopy, [&](const SkPaint& p) {
+        finalCanvas->drawTextBlob(builder.make(), 0, 0, paintCopy);
+    });
 }
 
 // ----------------------------------------------------------------------------
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index 0a54aca..e075d80 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -25,16 +25,16 @@
 static const int IDENT_DISPLAYEVENT = 1;
 
 static android::DisplayInfo DUMMY_DISPLAY{
-        1080,   // w
-        1920,   // h
-        320.0,  // xdpi
-        320.0,  // ydpi
-        60.0,   // fps
-        2.0,    // density
-        0,      // orientation
-        false,  // secure?
-        0,      // appVsyncOffset
-        0,      // presentationDeadline
+        1080,           // w
+        1920,           // h
+        320.0,          // xdpi
+        320.0,          // ydpi
+        60.0,           // fps
+        2.0,            // density
+        ui::ROTATION_0, // orientation
+        false,          // secure?
+        0,              // appVsyncOffset
+        0,              // presentationDeadline
 };
 
 DisplayInfo getInternalDisplay() {
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 8293b5f..cb132f5 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.util.SparseIntArray;
 
 import java.lang.annotation.Retention;
@@ -127,6 +128,23 @@
      * A device type describing a Hearing Aid.
      */
     public static final int TYPE_HEARING_AID   = 23;
+    /**
+     * A device type describing the speaker system (i.e. a mono speaker or stereo speakers) built
+     * in a device, that is specifically tuned for outputting sounds like notifications and alarms
+     * (i.e. sounds the user couldn't necessarily anticipate).
+     * <p>Note that this physical audio device may be the same as {@link #TYPE_BUILTIN_SPEAKER}
+     * but is driven differently to safely accommodate the different use case.</p>
+     */
+    public static final int TYPE_BUILTIN_SPEAKER_SAFE = 24;
+    /**
+     * @hide
+     * A device type for rerouting audio within the Android framework between mixes and
+     * system applications. Typically created when using
+     * {@link android.media.audiopolicy.AudioPolicy} for mixes created with the
+     * {@link android.media.audiopolicy.AudioMix#ROUTE_FLAG_RENDER} flag.
+     */
+    @SystemApi
+    public static final int TYPE_REMOTE_SUBMIX = 25;
 
     /** @hide */
     @IntDef(flag = false, prefix = "TYPE", value = {
@@ -228,6 +246,8 @@
             case TYPE_IP:
             case TYPE_BUS:
             case TYPE_HEARING_AID:
+            case TYPE_BUILTIN_SPEAKER_SAFE:
+            case TYPE_REMOTE_SUBMIX:
                 return true;
             default:
                 return false;
@@ -253,6 +273,7 @@
             case TYPE_LINE_DIGITAL:
             case TYPE_IP:
             case TYPE_BUS:
+            case TYPE_REMOTE_SUBMIX:
                 return true;
             default:
                 return false;
@@ -449,6 +470,9 @@
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_IP, TYPE_IP);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BUS, TYPE_BUS);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_HEARING_AID, TYPE_HEARING_AID);
+        INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_SPEAKER_SAFE,
+                TYPE_BUILTIN_SPEAKER_SAFE);
+        INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX, TYPE_REMOTE_SUBMIX);
 
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUILTIN_MIC, TYPE_BUILTIN_MIC);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET, TYPE_BLUETOOTH_SCO);
@@ -468,10 +492,7 @@
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, TYPE_BLUETOOTH_A2DP);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_IP, TYPE_IP);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUS, TYPE_BUS);
-
-        // not covered here, legacy
-        //AudioSystem.DEVICE_OUT_REMOTE_SUBMIX
-        //AudioSystem.DEVICE_IN_REMOTE_SUBMIX
+        INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_REMOTE_SUBMIX, TYPE_REMOTE_SUBMIX);
 
         // privileges mapping to output device
         EXT_TO_INT_DEVICE_MAPPING = new SparseIntArray();
@@ -498,6 +519,9 @@
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_IP, AudioSystem.DEVICE_OUT_IP);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BUS, AudioSystem.DEVICE_OUT_BUS);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_HEARING_AID, AudioSystem.DEVICE_OUT_HEARING_AID);
+        EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BUILTIN_SPEAKER_SAFE,
+                AudioSystem.DEVICE_OUT_SPEAKER_SAFE);
+        EXT_TO_INT_DEVICE_MAPPING.put(TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_OUT_REMOTE_SUBMIX);
     }
 }
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e410882..1b870e8 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4331,6 +4331,26 @@
         }
     }
 
+    /**
+     * @hide
+     * Get the audio devices that would be used for the routing of the given audio attributes.
+     * @param attributes the {@link AudioAttributes} for which the routing is being queried
+     * @return an empty list if there was an issue with the request, a list of audio devices
+     *   otherwise (typically one device, except for duplicated paths).
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public @NonNull List<AudioDeviceAddress> getDevicesForAttributes(
+            @NonNull AudioAttributes attributes) {
+        Objects.requireNonNull(attributes);
+        final IAudioService service = getService();
+        try {
+            return service.getDevicesForAttributes(attributes);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
      /**
      * Indicate wired accessory connection state change.
      * @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx)
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index e584add..fe57e71 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -33,6 +33,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /* IF YOU CHANGE ANY OF THE CONSTANTS IN THIS FILE, DO NOT FORGET
@@ -1077,6 +1078,41 @@
     @UnsupportedAppUsage
     public static native int getDevicesForStream(int stream);
 
+    /**
+     * Do not use directly, see {@link AudioManager#getDevicesForAttributes(AudioAttributes)}
+     * Get the audio devices that would be used for the routing of the given audio attributes.
+     * @param attributes the {@link AudioAttributes} for which the routing is being queried
+     * @return an empty list if there was an issue with the request, a list of audio devices
+     *   otherwise (typically one device, except for duplicated paths).
+     */
+    public static @NonNull ArrayList<AudioDeviceAddress> getDevicesForAttributes(
+            @NonNull AudioAttributes attributes) {
+        Objects.requireNonNull(attributes);
+        final AudioDeviceAddress[] devices = new AudioDeviceAddress[MAX_DEVICE_ROUTING];
+        final int res = getDevicesForAttributes(attributes, devices);
+        final ArrayList<AudioDeviceAddress> routeDevices = new ArrayList<>();
+        if (res != SUCCESS) {
+            Log.e(TAG, "error " + res + " in getDevicesForAttributes for " + attributes);
+            return routeDevices;
+        }
+
+        for (AudioDeviceAddress device : devices) {
+            if (device != null) {
+                routeDevices.add(device);
+            }
+        }
+        return routeDevices;
+    }
+
+    /**
+     * Maximum number of audio devices a track is ever routed to, determines the size of the
+     * array passed to {@link #getDevicesForAttributes(AudioAttributes, AudioDeviceAddress[])}
+     */
+    private static final int MAX_DEVICE_ROUTING = 4;
+
+    private static native int getDevicesForAttributes(@NonNull AudioAttributes aa,
+                                                      @NonNull AudioDeviceAddress[] devices);
+
     /** @hide returns true if master mono is enabled. */
     public static native boolean getMasterMono();
     /** @hide enables or disables the master mono mode. */
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index ad7335e..8e8385d 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -272,6 +272,8 @@
 
     AudioDeviceAddress getPreferredDeviceForStrategy(in int strategy);
 
+    List<AudioDeviceAddress> getDevicesForAttributes(in AudioAttributes attributes);
+
     // WARNING: read warning at top of file, new methods that need to be used by native
     // code via IAudioManager.h need to be added to the top section.
 }
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index 28bf84d..5dd0b1c 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -24,7 +24,8 @@
  */
 oneway interface IMediaRoute2Provider {
     void setClient(IMediaRoute2ProviderClient client);
-    void requestCreateSession(String packageName, String routeId, String routeType, long requestId);
+    void requestCreateSession(String packageName, String routeId,
+            String routeFeature, long requestId);
     void releaseSession(String sessionId);
 
     void selectRoute(String sessionId, String routeId);
diff --git a/media/java/android/media/IMediaRoute2ProviderClient.aidl b/media/java/android/media/IMediaRoute2ProviderClient.aidl
index bcb2336..e8abfdc 100644
--- a/media/java/android/media/IMediaRoute2ProviderClient.aidl
+++ b/media/java/android/media/IMediaRoute2ProviderClient.aidl
@@ -18,7 +18,7 @@
 
 import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRoute2Info;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
 import android.os.Bundle;
 
 /**
@@ -26,7 +26,7 @@
  */
 oneway interface IMediaRoute2ProviderClient {
     void updateState(in MediaRoute2ProviderInfo providerInfo,
-            in List<RouteSessionInfo> sessionInfos);
-    void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, long requestId);
-    void notifySessionInfoChanged(in RouteSessionInfo sessionInfo);
+            in List<RoutingSessionInfo> sessionInfos);
+    void notifySessionCreated(in @nullable RoutingSessionInfo sessionInfo, long requestId);
+    void notifySessionInfoChanged(in RoutingSessionInfo sessionInfo);
 }
diff --git a/media/java/android/media/IMediaRouter2Client.aidl b/media/java/android/media/IMediaRouter2Client.aidl
index f90c7c5..bc7ebea 100644
--- a/media/java/android/media/IMediaRouter2Client.aidl
+++ b/media/java/android/media/IMediaRouter2Client.aidl
@@ -17,7 +17,7 @@
 package android.media;
 
 import android.media.MediaRoute2Info;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
 import android.os.Bundle;
 
 /**
@@ -28,7 +28,7 @@
     void notifyRoutesAdded(in List<MediaRoute2Info> routes);
     void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
     void notifyRoutesChanged(in List<MediaRoute2Info> routes);
-    void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, int requestId);
-    void notifySessionInfoChanged(in RouteSessionInfo sessionInfo);
-    void notifySessionReleased(in RouteSessionInfo sessionInfo);
+    void notifySessionCreated(in @nullable RoutingSessionInfo sessionInfo, int requestId);
+    void notifySessionInfoChanged(in RoutingSessionInfo sessionInfo);
+    void notifySessionReleased(in RoutingSessionInfo sessionInfo);
 }
diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl
index e8af21e..e9add17 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 notifyRouteTypesChanged(String packageName, in List<String> routeTypes);
+    void notifyPreferredFeaturesChanged(String packageName, in List<String> preferredFeatures);
     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 b573f64..3cdaa07 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -22,8 +22,8 @@
 import android.media.IMediaRouterClient;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouterClientState;
-import android.media.RouteDiscoveryRequest;
-import android.media.RouteSessionInfo;
+import android.media.RouteDiscoveryPreference;
+import android.media.RoutingSessionInfo;
 
 /**
  * {@hide}
@@ -52,8 +52,8 @@
     void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
 
     void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route,
-            String routeType, int requestId);
-    void setDiscoveryRequest2(IMediaRouter2Client client, in RouteDiscoveryRequest request);
+            String routeFeature, int requestId);
+    void setDiscoveryRequest2(IMediaRouter2Client client, in RouteDiscoveryPreference preference);
     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);
@@ -70,5 +70,5 @@
     void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
             in MediaRoute2Info route, int direction);
 
-    List<RouteSessionInfo> getActiveSessions(IMediaRouter2Manager manager);
+    List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager);
 }
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 1ed53d9..021e23e 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -36,7 +36,6 @@
 
 /**
  * Describes the properties of a route.
- * @hide
  */
 public final class MediaRoute2Info implements Parcelable {
     @NonNull
@@ -56,7 +55,7 @@
     @IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING,
             CONNECTION_STATE_CONNECTED})
     @Retention(RetentionPolicy.SOURCE)
-    private @interface ConnectionState {}
+    public @interface ConnectionState {}
 
     /**
      * The default connection state indicating the route is disconnected.
@@ -99,16 +98,15 @@
 
     /** @hide */
     @IntDef({
-            DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV,
-            DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH})
+            DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_REMOTE_TV,
+            DEVICE_TYPE_REMOTE_SPEAKER, DEVICE_TYPE_BLUETOOTH})
     @Retention(RetentionPolicy.SOURCE)
-    private @interface DeviceType {}
+    public @interface DeviceType {}
 
     /**
      * The default receiver device type of the route indicating the type is unknown.
      *
      * @see #getDeviceType
-     * @hide
      */
     public static final int DEVICE_TYPE_UNKNOWN = 0;
 
@@ -118,7 +116,7 @@
      *
      * @see #getDeviceType
      */
-    public static final int DEVICE_TYPE_TV = 1;
+    public static final int DEVICE_TYPE_REMOTE_TV = 1;
 
     /**
      * A receiver device type of the route indicating the presentation of the media is happening
@@ -126,14 +124,13 @@
      *
      * @see #getDeviceType
      */
-    public static final int DEVICE_TYPE_SPEAKER = 2;
+    public static final int DEVICE_TYPE_REMOTE_SPEAKER = 2;
 
     /**
      * A receiver device type of the route indicating the presentation of the media is happening
      * on a bluetooth device such as a bluetooth speaker.
      *
      * @see #getDeviceType
-     * @hide
      */
     public static final int DEVICE_TYPE_BLUETOOTH = 3;
 
@@ -152,7 +149,7 @@
     @Nullable
     final String mClientPackageName;
     @NonNull
-    final List<String> mRouteTypes;
+    final List<String> mFeatures;
     final int mVolume;
     final int mVolumeMax;
     final int mVolumeHandling;
@@ -168,7 +165,7 @@
         mConnectionState = builder.mConnectionState;
         mIconUri = builder.mIconUri;
         mClientPackageName = builder.mClientPackageName;
-        mRouteTypes = builder.mRouteTypes;
+        mFeatures = builder.mFeatures;
         mVolume = builder.mVolume;
         mVolumeMax = builder.mVolumeMax;
         mVolumeHandling = builder.mVolumeHandling;
@@ -184,7 +181,7 @@
         mConnectionState = in.readInt();
         mIconUri = in.readParcelable(null);
         mClientPackageName = in.readString();
-        mRouteTypes = in.createStringArrayList();
+        mFeatures = in.createStringArrayList();
         mVolume = in.readInt();
         mVolumeMax = in.readInt();
         mVolumeHandling = in.readInt();
@@ -223,7 +220,7 @@
                 && (mConnectionState == other.mConnectionState)
                 && Objects.equals(mIconUri, other.mIconUri)
                 && Objects.equals(mClientPackageName, other.mClientPackageName)
-                && Objects.equals(mRouteTypes, other.mRouteTypes)
+                && Objects.equals(mFeatures, other.mFeatures)
                 && (mVolume == other.mVolume)
                 && (mVolumeMax == other.mVolumeMax)
                 && (mVolumeHandling == other.mVolumeHandling)
@@ -235,7 +232,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(mId, mName, mDescription, mConnectionState, mIconUri,
-                mRouteTypes, mVolume, mVolumeMax, mVolumeHandling, mDeviceType);
+                mFeatures, mVolume, mVolumeMax, mVolumeHandling, mDeviceType);
     }
 
     /**
@@ -245,7 +242,7 @@
      * 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#setId(String)
+     * @see Builder#Builder(String, CharSequence)
      */
     @NonNull
     public String getId() {
@@ -257,7 +254,7 @@
     }
 
     /**
-     * Gets the original id set by {@link Builder#setId(String)}.
+     * Gets the original id set by {@link Builder#Builder(String, CharSequence)}.
      * @hide
      */
     @NonNull
@@ -324,16 +321,16 @@
      * Gets the supported categories of the route.
      */
     @NonNull
-    public List<String> getRouteTypes() {
-        return mRouteTypes;
+    public List<String> getFeatures() {
+        return mFeatures;
     }
 
-    //TODO: once device types are confirmed, reflect those into the comment.
     /**
      * Gets the type of the receiver device associated with this route.
      *
      * @return The type of the receiver device associated with this route:
-     * {@link #DEVICE_TYPE_TV} or {@link #DEVICE_TYPE_SPEAKER}.
+     * {@link #DEVICE_TYPE_REMOTE_TV}, {@link #DEVICE_TYPE_REMOTE_SPEAKER},
+     * {@link #DEVICE_TYPE_BLUETOOTH}.
      */
     @DeviceType
     public int getDeviceType() {
@@ -369,15 +366,15 @@
     }
 
     /**
-     * Returns if the route contains at least one of the specified route types.
+     * Returns if the route has at least one of the specified route features.
      *
-     * @param routeTypes the list of route types to consider
-     * @return true if the route contains at least one type in the list
+     * @param features the list of route features to consider
+     * @return true if the route has at least one feature in the list
      */
-    public boolean containsRouteTypes(@NonNull Collection<String> routeTypes) {
-        Objects.requireNonNull(routeTypes, "routeTypes must not be null");
-        for (String routeType : routeTypes) {
-            if (getRouteTypes().contains(routeType)) {
+    public boolean hasAnyFeatures(@NonNull Collection<String> features) {
+        Objects.requireNonNull(features, "features must not be null");
+        for (String feature : features) {
+            if (getFeatures().contains(feature)) {
                 return true;
             }
         }
@@ -398,7 +395,7 @@
         dest.writeInt(mConnectionState);
         dest.writeParcelable(mIconUri, flags);
         dest.writeString(mClientPackageName);
-        dest.writeStringList(mRouteTypes);
+        dest.writeStringList(mFeatures);
         dest.writeInt(mVolume);
         dest.writeInt(mVolumeMax);
         dest.writeInt(mVolumeHandling);
@@ -428,15 +425,15 @@
      * Builder for {@link MediaRoute2Info media route info}.
      */
     public static final class Builder {
-        String mId;
+        final String mId;
         String mProviderId;
-        CharSequence mName;
+        final CharSequence mName;
         CharSequence mDescription;
         @ConnectionState
         int mConnectionState;
         Uri mIconUri;
         String mClientPackageName;
-        List<String> mRouteTypes;
+        List<String> mFeatures;
         int mVolume;
         int mVolumeMax;
         int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
@@ -444,27 +441,43 @@
         int mDeviceType = DEVICE_TYPE_UNKNOWN;
         Bundle mExtras;
 
+        /**
+         * Constructor for builder to create {@link MediaRoute2Info}.
+         * <p>
+         * In order to ensure ID uniqueness, the {@link MediaRoute2Info#getId() ID} of a route info
+         * obtained from {@link MediaRouter2} can be different from what was set in
+         * {@link MediaRoute2ProviderService}.
+         * </p>
+         * @param id
+         * @param name
+         */
         public Builder(@NonNull String id, @NonNull CharSequence name) {
-            setId(id);
-            setName(name);
-            mRouteTypes = new ArrayList<>();
+            if (TextUtils.isEmpty(id)) {
+                throw new IllegalArgumentException("id must not be empty");
+            }
+            if (TextUtils.isEmpty(name)) {
+                throw new IllegalArgumentException("name must not be empty");
+            }
+            mId = id;
+            mName = name;
+            mFeatures = new ArrayList<>();
         }
 
         public Builder(@NonNull MediaRoute2Info routeInfo) {
             if (routeInfo == null) {
                 throw new IllegalArgumentException("route info must not be null");
             }
+            mId = routeInfo.mId;
+            mName = routeInfo.mName;
 
-            setId(routeInfo.mId);
             if (!TextUtils.isEmpty(routeInfo.mProviderId)) {
                 setProviderId(routeInfo.mProviderId);
             }
-            setName(routeInfo.mName);
             mDescription = routeInfo.mDescription;
             mConnectionState = routeInfo.mConnectionState;
             mIconUri = routeInfo.mIconUri;
             setClientPackageName(routeInfo.mClientPackageName);
-            setRouteTypes(routeInfo.mRouteTypes);
+            mFeatures = new ArrayList<>(routeInfo.mFeatures);
             setVolume(routeInfo.mVolume);
             setVolumeMax(routeInfo.mVolumeMax);
             setVolumeHandling(routeInfo.mVolumeHandling);
@@ -475,26 +488,6 @@
         }
 
         /**
-         * 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) {
-            if (TextUtils.isEmpty(id)) {
-                throw new IllegalArgumentException("id must not be null or empty");
-            }
-            mId = id;
-            return this;
-        }
-
-        /**
          * Sets the provider id of the route.
          * @hide
          */
@@ -508,20 +501,10 @@
         }
 
         /**
-         * Sets the user-visible name of the route.
-         */
-        @NonNull
-        public Builder setName(@NonNull CharSequence name) {
-            Objects.requireNonNull(name, "name must not be null");
-            mName = name;
-            return this;
-        }
-
-        /**
          * Sets the user-visible description of the route.
          */
         @NonNull
-        public Builder setDescription(@Nullable String description) {
+        public Builder setDescription(@Nullable CharSequence description) {
             mDescription = description;
             return this;
         }
@@ -569,35 +552,35 @@
         }
 
         /**
-         * Sets the types of the route.
+         * Clears the features of the route.
          */
         @NonNull
-        public Builder setRouteTypes(@NonNull Collection<String> routeTypes) {
-            mRouteTypes = new ArrayList<>();
-            return addRouteTypes(routeTypes);
+        public Builder clearFeatures() {
+            mFeatures = new ArrayList<>();
+            return this;
         }
 
         /**
-         * Adds types for the route.
+         * Adds features for the route.
          */
         @NonNull
-        public Builder addRouteTypes(@NonNull Collection<String> routeTypes) {
-            Objects.requireNonNull(routeTypes, "routeTypes must not be null");
-            for (String routeType: routeTypes) {
-                addRouteType(routeType);
+        public Builder addFeatures(@NonNull Collection<String> features) {
+            Objects.requireNonNull(features, "features must not be null");
+            for (String feature : features) {
+                addFeature(feature);
             }
             return this;
         }
 
         /**
-         * Add a type for the route.
+         * Adds a feature for the route.
          */
         @NonNull
-        public Builder addRouteType(@NonNull String routeType) {
-            if (TextUtils.isEmpty(routeType)) {
-                throw new IllegalArgumentException("routeType must not be null or empty");
+        public Builder addFeature(@NonNull String feature) {
+            if (TextUtils.isEmpty(feature)) {
+                throw new IllegalArgumentException("feature must not be null or empty");
             }
-            mRouteTypes.add(routeType);
+            mFeatures.add(feature);
             return this;
         }
 
@@ -642,7 +625,7 @@
          */
         @NonNull
         public Builder setExtras(@Nullable Bundle extras) {
-            mExtras = extras;
+            mExtras = new Bundle(extras);
             return this;
         }
 
diff --git a/media/java/android/media/MediaRoute2ProviderInfo.java b/media/java/android/media/MediaRoute2ProviderInfo.java
index e2f246c..c9a2ec7 100644
--- a/media/java/android/media/MediaRoute2ProviderInfo.java
+++ b/media/java/android/media/MediaRoute2ProviderInfo.java
@@ -93,6 +93,9 @@
 
     /**
      * Gets the route for the given route id or null if no matching route exists.
+     * Please note that id should be original id.
+     *
+     * @see MediaRoute2Info#getOriginalId()
      */
     @Nullable
     public MediaRoute2Info getRoute(@NonNull String routeId) {
@@ -168,7 +171,7 @@
                 MediaRoute2Info routeWithProviderId = new MediaRoute2Info.Builder(entry.getValue())
                         .setProviderId(mUniqueId)
                         .build();
-                newRoutes.put(routeWithProviderId.getId(), routeWithProviderId);
+                newRoutes.put(routeWithProviderId.getOriginalId(), routeWithProviderId);
             }
 
             mRoutes.clear();
@@ -183,14 +186,14 @@
         public Builder addRoute(@NonNull MediaRoute2Info route) {
             Objects.requireNonNull(route, "route must not be null");
 
-            if (mRoutes.containsValue(route)) {
-                throw new IllegalArgumentException("route descriptor already added");
+            if (mRoutes.containsKey(route.getOriginalId())) {
+                throw new IllegalArgumentException("A route with the same id is already added");
             }
             if (mUniqueId != null) {
-                mRoutes.put(route.getId(),
+                mRoutes.put(route.getOriginalId(),
                         new MediaRoute2Info.Builder(route).setProviderId(mUniqueId).build());
             } else {
-                mRoutes.put(route.getId(), route);
+                mRoutes.put(route.getOriginalId(), route);
             }
             return this;
         }
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 24b65ba..1cd5dfa 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -36,12 +36,22 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * @hide
+ * Base class for media route provider services.
+ * <p>
+ * The system media router service will bind to media route provider services when a
+ * {@link RouteDiscoveryPreference discovery preference} is registered via
+ * a {@link MediaRouter2 media router} by an application.
+ * </p><p>
+ * To implement your own media route provider service, extend this class and
+ * override {@link #onDiscoveryPreferenceChanged(RouteDiscoveryPreference)} to publish
+ * {@link MediaRoute2Info routes}.
+ * </p>
  */
 public abstract class MediaRoute2ProviderService extends Service {
     private static final String TAG = "MR2ProviderService";
@@ -56,13 +66,14 @@
     private MediaRoute2ProviderInfo mProviderInfo;
 
     @GuardedBy("mSessionLock")
-    private ArrayMap<String, RouteSessionInfo> mSessionInfo = new ArrayMap<>();
+    private ArrayMap<String, RoutingSessionInfo> mSessionInfo = new ArrayMap<>();
 
     public MediaRoute2ProviderService() {
         mHandler = new Handler(Looper.getMainLooper());
     }
 
     @Override
+    @NonNull
     public IBinder onBind(@NonNull Intent intent) {
         //TODO: Allow binding from media router service only?
         if (SERVICE_INTERFACE.equals(intent.getAction())) {
@@ -79,6 +90,7 @@
      *
      * @param routeId the id of the target route
      * @param request the media control request intent
+     * @hide
      */
     //TODO: Discuss what to use for request (e.g., Intent? Request class?)
     public abstract void onControlRequest(@NonNull String routeId, @NonNull Intent request);
@@ -88,6 +100,7 @@
      *
      * @param routeId the id of the route
      * @param volume the target volume
+     * @hide
      */
     public abstract void onSetVolume(@NonNull String routeId, int volume);
 
@@ -96,6 +109,7 @@
      *
      * @param routeId id of the route
      * @param delta the delta to add to the current volume
+     * @hide
      */
     public abstract void onUpdateVolume(@NonNull String routeId, int delta);
 
@@ -105,9 +119,10 @@
      * @param sessionId id of the session
      * @return information of the session with the given id.
      *         null if the session is destroyed or id is not valid.
+     * @hide
      */
     @Nullable
-    public final RouteSessionInfo getSessionInfo(@NonNull String sessionId) {
+    public final RoutingSessionInfo getSessionInfo(@NonNull String sessionId) {
         if (TextUtils.isEmpty(sessionId)) {
             throw new IllegalArgumentException("sessionId must not be empty");
         }
@@ -117,10 +132,11 @@
     }
 
     /**
-     * Gets the list of {@link RouteSessionInfo session info} that the provider service maintains.
+     * Gets the list of {@link RoutingSessionInfo session info} that the provider service maintains.
+     * @hide
      */
     @NonNull
-    public final List<RouteSessionInfo> getAllSessionInfo() {
+    public final List<RoutingSessionInfo> getAllSessionInfo() {
         synchronized (mSessionLock) {
             return new ArrayList<>(mSessionInfo.values());
         }
@@ -129,20 +145,16 @@
     /**
      * Updates the information of a session.
      * If the session is destroyed or not created before, it will be ignored.
-     * A session will be destroyed if it has no selected route.
      * Call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify clients of
      * session info changes.
      *
      * @param sessionInfo new session information
-     * @see #notifySessionCreated(RouteSessionInfo, long)
+     * @see #notifySessionCreated(RoutingSessionInfo, long)
+     * @hide
      */
-    public final void updateSessionInfo(@NonNull RouteSessionInfo sessionInfo) {
+    public final void updateSessionInfo(@NonNull RoutingSessionInfo sessionInfo) {
         Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
         String sessionId = sessionInfo.getId();
-        if (sessionInfo.getSelectedRoutes().isEmpty()) {
-            releaseSession(sessionId);
-            return;
-        }
 
         synchronized (mSessionLock) {
             if (mSessionInfo.containsKey(sessionId)) {
@@ -161,7 +173,7 @@
      * TODO: This method is temporary, only created for tests. Remove when the alternative is ready.
      * @hide
      */
-    public final void notifySessionInfoChanged(@NonNull RouteSessionInfo sessionInfo) {
+    public final void notifySessionInfoChanged(@NonNull RoutingSessionInfo sessionInfo) {
         Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
 
         String sessionId = sessionInfo.getId();
@@ -189,14 +201,16 @@
      * controlled, pass a {@link Bundle} that contains how to control it.
      *
      * @param sessionInfo information of the new session.
-     *                    The {@link RouteSessionInfo#getId() id} of the session must be
+     *                    The {@link RoutingSessionInfo#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
+     * @hide
      */
     // TODO: fail reason?
     // TODO: Maybe better to create notifySessionCreationFailed?
-    public final void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) {
+    public final void notifySessionCreated(@Nullable RoutingSessionInfo sessionInfo,
+            long requestId) {
         if (sessionInfo != null) {
             String sessionId = sessionInfo.getId();
             synchronized (mSessionLock) {
@@ -224,14 +238,15 @@
      * {@link #onDestroySession} is called if the session is released.
      *
      * @param sessionId id of the session to be released
-     * @see #onDestroySession(String, RouteSessionInfo)
+     * @see #onDestroySession(String, RoutingSessionInfo)
+     * @hide
      */
     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;
+        RoutingSessionInfo sessionInfo;
         synchronized (mSessionLock) {
             sessionInfo = mSessionInfo.remove(sessionId);
         }
@@ -245,19 +260,20 @@
     /**
      * Called when a session should be created.
      * You should create and maintain your own session and notifies the client of
-     * session info. Call {@link #notifySessionCreated(RouteSessionInfo, long)}
+     * session info. Call {@link #notifySessionCreated(RoutingSessionInfo, long)}
      * with the given {@code requestId} to notify the information of a new session.
      * If you can't create the session or want to reject the request, pass {@code null}
-     * as session info in {@link #notifySessionCreated(RouteSessionInfo, long)}
+     * as session info in {@link #notifySessionCreated(RoutingSessionInfo, long)}
      * with the given {@code requestId}.
      *
      * @param packageName the package name of the application that selected the route
      * @param routeId the id of the route initially being connected
-     * @param routeType the route type of the new session
+     * @param routeFeature the route feature of the new session
      * @param requestId the id of this session creation request
+     * @hide
      */
     public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId,
-            @NonNull String routeType, long requestId);
+            @NonNull String routeFeature, long requestId);
 
     /**
      * Called when a session is about to be destroyed.
@@ -267,71 +283,78 @@
      * @param sessionId id of the session being destroyed.
      * @param lastSessionInfo information of the session being destroyed.
      * @see #releaseSession(String)
+     * @hide
      */
     public abstract void onDestroySession(@NonNull String sessionId,
-            @NonNull RouteSessionInfo lastSessionInfo);
+            @NonNull RoutingSessionInfo lastSessionInfo);
 
     //TODO: make a way to reject the request
     /**
      * Called when a client requests selecting a route for the session.
-     * After the route is selected, call {@link #updateSessionInfo(RouteSessionInfo)} to update
+     * After the route is selected, call {@link #updateSessionInfo(RoutingSessionInfo)} to update
      * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify
      * clients of updated session info.
      *
      * @param sessionId id of the session
      * @param routeId id of the route
-     * @see #updateSessionInfo(RouteSessionInfo)
+     * @see #updateSessionInfo(RoutingSessionInfo)
+     * @hide
      */
     public abstract void onSelectRoute(@NonNull String sessionId, @NonNull String routeId);
 
     //TODO: make a way to reject the request
     /**
      * Called when a client requests deselecting a route from the session.
-     * After the route is deselected, call {@link #updateSessionInfo(RouteSessionInfo)} to update
+     * After the route is deselected, call {@link #updateSessionInfo(RoutingSessionInfo)} to update
      * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify
      * clients of updated session info.
      *
      * @param sessionId id of the session
      * @param routeId id of the route
+     * @hide
      */
     public abstract void onDeselectRoute(@NonNull String sessionId, @NonNull String routeId);
 
     //TODO: make a way to reject the request
     /**
      * Called when a client requests transferring a session to a route.
-     * After the transfer is finished, call {@link #updateSessionInfo(RouteSessionInfo)} to update
+     * After the transfer is finished, call {@link #updateSessionInfo(RoutingSessionInfo)} to update
      * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify
      * clients of updated session info.
      *
      * @param sessionId id of the session
      * @param routeId id of the route
+     * @hide
      */
     public abstract void onTransferToRoute(@NonNull String sessionId, @NonNull String routeId);
 
     /**
-     * Called when the {@link RouteDiscoveryRequest discovery request} has changed.
+     * Called when the {@link RouteDiscoveryPreference discovery preference} 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.
+     * it also provides a discovery preference to specify features of routes that it is interested
+     * in. The media router combines all of these discovery request into a single discovery
+     * preference 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.
+     * The provider should examine {@link RouteDiscoveryPreference#getPreferredFeatures()
+     * preferred features} in the discovery preference 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 features.
      * </p>
      *
-     * @param request the new discovery request
+     * @param preference the new discovery preference
      */
-    public void onDiscoveryRequestChanged(@NonNull RouteDiscoveryRequest request) {}
+    public void onDiscoveryPreferenceChanged(@NonNull RouteDiscoveryPreference preference) {}
 
     /**
-     * Updates provider info and publishes routes and session info.
+     * Updates routes of the provider and notifies the system media router service.
      */
-    public final void updateProviderInfo(@NonNull MediaRoute2ProviderInfo providerInfo) {
-        mProviderInfo = Objects.requireNonNull(providerInfo, "providerInfo must not be null");
+    public final void notifyRoutes(@NonNull Collection<MediaRoute2Info> routes) {
+        Objects.requireNonNull(routes, "routes must not be null");
+        mProviderInfo = new MediaRoute2ProviderInfo.Builder()
+                .addRoutes(routes)
+                .build();
         schedulePublishState();
     }
 
@@ -355,7 +378,7 @@
             return;
         }
 
-        List<RouteSessionInfo> sessionInfos;
+        List<RoutingSessionInfo> sessionInfos;
         synchronized (mSessionLock) {
             sessionInfos = new ArrayList<>(mSessionInfo.values());
         }
@@ -384,12 +407,12 @@
 
         @Override
         public void requestCreateSession(String packageName, String routeId,
-                String routeType, long requestId) {
+                String routeFeature, long requestId) {
             if (!checkCallerisSystem()) {
                 return;
             }
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
-                    MediaRoute2ProviderService.this, packageName, routeId, routeType,
+                    MediaRoute2ProviderService.this, packageName, routeId, routeFeature,
                     requestId));
         }
         @Override
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 8ebf617..6d37c2d 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -18,10 +18,7 @@
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
 import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -37,7 +34,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 
-import java.lang.annotation.Retention;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -50,50 +46,13 @@
 import java.util.stream.Collectors;
 
 /**
- * A new Media Router
- * @hide
+ * Media Router 2 allows applications to control the routing of media channels
+ * and streams from the current device to remote speakers and devices.
  *
  * TODO: Add method names at the beginning of log messages. (e.g. changeSessionInfoOnHandler)
  *       Not only MediaRouter2, but also to service / manager / provider.
  */
 public class MediaRouter2 {
-
-    /** @hide */
-    @Retention(SOURCE)
-    @IntDef(value = {
-            SELECT_REASON_UNKNOWN,
-            SELECT_REASON_USER_SELECTED,
-            SELECT_REASON_FALLBACK,
-            SELECT_REASON_SYSTEM_SELECTED})
-    public @interface SelectReason {}
-
-    /**
-     * Passed to {@link Callback#onRouteSelected(MediaRoute2Info, int, Bundle)} when the reason
-     * the route was selected is unknown.
-     */
-    public static final int SELECT_REASON_UNKNOWN = 0;
-
-    /**
-     * Passed to {@link Callback#onRouteSelected(MediaRoute2Info, int, Bundle)} when the route
-     * is selected in response to a user's request. For example, when a user has selected
-     * a different device to play media to.
-     */
-    public static final int SELECT_REASON_USER_SELECTED = 1;
-
-    /**
-     * Passed to {@link Callback#onRouteSelected(MediaRoute2Info, int, Bundle)} when the route
-     * is selected as a fallback route. For example, when Wi-Fi is disconnected, the device speaker
-     * may be selected as a fallback route.
-     */
-    public static final int SELECT_REASON_FALLBACK = 2;
-
-    /**
-     * This is passed from {@link com.android.server.media.MediaRouterService} when the route
-     * is selected in response to a request from other apps (e.g. System UI).
-     * @hide
-     */
-    public static final int SELECT_REASON_SYSTEM_SELECTED = 3;
-
     private static final String TAG = "MR2";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final Object sRouterLock = new Object();
@@ -118,14 +77,14 @@
     final Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
 
     @GuardedBy("sLock")
-    private RouteDiscoveryRequest mDiscoveryRequest = RouteDiscoveryRequest.EMPTY;
+    private RouteDiscoveryPreference mDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
 
     // TODO: Make MediaRouter2 is always connected to the MediaRouterService.
     @GuardedBy("sLock")
     Client2 mClient;
 
     @GuardedBy("sLock")
-    private Map<String, RouteSessionController> mSessionControllers = new ArrayMap<>();
+    private Map<String, RoutingController> mRoutingControllers = new ArrayMap<>();
 
     private AtomicInteger mSessionCreationRequestCnt = new AtomicInteger(1);
 
@@ -137,6 +96,7 @@
     /**
      * Gets an instance of the media router associated with the context.
      */
+    @NonNull
     public static MediaRouter2 getInstance(@NonNull Context context) {
         Objects.requireNonNull(context, "context must not be null");
         synchronized (sRouterLock) {
@@ -193,12 +153,12 @@
      */
     public void registerRouteCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull RouteCallback routeCallback,
-            @NonNull RouteDiscoveryRequest request) {
+            @NonNull RouteDiscoveryPreference preference) {
         Objects.requireNonNull(executor, "executor must not be null");
         Objects.requireNonNull(routeCallback, "callback must not be null");
-        Objects.requireNonNull(request, "request must not be null");
+        Objects.requireNonNull(preference, "preference must not be null");
 
-        RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, request);
+        RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, preference);
         if (!mRouteCallbackRecords.addIfAbsent(record)) {
             Log.w(TAG, "Ignoring the same callback");
             return;
@@ -210,15 +170,13 @@
                 try {
                     mMediaRouterService.registerClient2(client, mPackageName);
                     updateDiscoveryRequestLocked();
-                    mMediaRouterService.setDiscoveryRequest2(client, mDiscoveryRequest);
+                    mMediaRouterService.setDiscoveryRequest2(client, mDiscoveryPreference);
                     mClient = client;
                 } catch (RemoteException ex) {
                     Log.e(TAG, "Unable to register media router.", ex);
                 }
             }
         }
-
-        //TODO: Update discovery request here.
     }
 
     /**
@@ -251,8 +209,8 @@
     }
 
     private void updateDiscoveryRequestLocked() {
-        mDiscoveryRequest = new RouteDiscoveryRequest.Builder(
-                mRouteCallbackRecords.stream().map(record -> record.mRequest).collect(
+        mDiscoveryPreference = new RouteDiscoveryPreference.Builder(
+                mRouteCallbackRecords.stream().map(record -> record.mPreference).collect(
                         Collectors.toList())).build();
     }
 
@@ -261,8 +219,8 @@
      * known to the media router.
      * Please note that the list can be changed before callbacks are invoked.
      *
-     * @return the list of routes that contains at least one of the route types in discovery
-     * requests registered by the application
+     * @return the list of routes that contains at least one of the route features in discovery
+     * preferences registered by the application
      */
     @NonNull
     public List<MediaRoute2Info> getRoutes() {
@@ -272,7 +230,7 @@
 
                 List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
                 for (MediaRoute2Info route : mRoutes.values()) {
-                    if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) {
+                    if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
                         filteredRoutes.add(route);
                     }
                 }
@@ -283,12 +241,13 @@
     }
 
     /**
-     * Registers a callback to get updates on creations and changes of route sessions.
+     * Registers a callback to get updates on creations and changes of routing sessions.
      * If you register the same callback twice or more, it will be ignored.
      *
      * @param executor the executor to execute the callback on
      * @param callback the callback to register
      * @see #unregisterSessionCallback
+     * @hide
      */
     @NonNull
     public void registerSessionCallback(@CallbackExecutor Executor executor,
@@ -309,6 +268,7 @@
      *
      * @param callback the callback to unregister
      * @see #registerSessionCallback
+     * @hide
      */
     @NonNull
     public void unregisterSessionCallback(@NonNull SessionCallback callback) {
@@ -324,25 +284,26 @@
      * 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 routeType the route type of the session. Should not be empty
+     * @param routeFeature the route feature of the session. Should not be empty.
      *
      * @see SessionCallback#onSessionCreated
      * @see SessionCallback#onSessionCreationFailed
+     * @hide
      */
     @NonNull
     public void requestCreateSession(@NonNull MediaRoute2Info route,
-            @NonNull String routeType) {
+            @NonNull String routeFeature) {
         Objects.requireNonNull(route, "route must not be null");
-        if (TextUtils.isEmpty(routeType)) {
-            throw new IllegalArgumentException("routeType must not be empty");
+        if (TextUtils.isEmpty(routeFeature)) {
+            throw new IllegalArgumentException("routeFeature must not be empty");
         }
         // TODO: Check the given route exists
-        // TODO: Check the route supports the given routeType
+        // TODO: Check the route supports the given routeFeature
 
         final int requestId;
         requestId = mSessionCreationRequestCnt.getAndIncrement();
 
-        SessionCreationRequest request = new SessionCreationRequest(requestId, route, routeType);
+        SessionCreationRequest request = new SessionCreationRequest(requestId, route, routeFeature);
         mSessionCreationRequests.add(request);
 
         Client2 client;
@@ -351,7 +312,7 @@
         }
         if (client != null) {
             try {
-                mMediaRouterService.requestCreateSession(client, route, routeType, requestId);
+                mMediaRouterService.requestCreateSession(client, route, routeFeature, requestId);
             } catch (RemoteException ex) {
                 Log.e(TAG, "Unable to request to create session.", ex);
                 mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
@@ -365,6 +326,7 @@
      *
      * @param route the route that will receive the control request
      * @param request the media control request
+     * @hide
      */
     //TODO: Discuss what to use for request (e.g., Intent? Request class?)
     //TODO: Provide a way to obtain the result
@@ -392,6 +354,7 @@
      * </p>
      *
      * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
+     * @hide
      */
     public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
         Objects.requireNonNull(route, "route must not be null");
@@ -416,6 +379,7 @@
      * </p>
      *
      * @param delta The delta to add to the current volume.
+     * @hide
      */
     public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) {
         Objects.requireNonNull(route, "route must not be null");
@@ -442,7 +406,7 @@
         synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.put(route.getId(), route);
-                if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) {
+                if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
                     addedRoutes.add(route);
                 }
             }
@@ -458,7 +422,7 @@
         synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.remove(route.getId());
-                if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) {
+                if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
                     removedRoutes.add(route);
                 }
             }
@@ -474,7 +438,7 @@
         synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.put(route.getId(), route);
-                if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) {
+                if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
                     changedRoutes.add(route);
                 }
             }
@@ -491,7 +455,7 @@
      * <p>
      * Pass {@code null} to sessionInfo for the failure case.
      */
-    void createControllerOnHandler(@Nullable RouteSessionInfo sessionInfo, int requestId) {
+    void createControllerOnHandler(@Nullable RoutingSessionInfo sessionInfo, int requestId) {
         SessionCreationRequest matchingRequest = null;
         for (SessionCreationRequest request : mSessionCreationRequests) {
             if (request.mRequestId == requestId) {
@@ -504,27 +468,27 @@
             mSessionCreationRequests.remove(matchingRequest);
 
             MediaRoute2Info requestedRoute = matchingRequest.mRoute;
-            String requestedRouteType = matchingRequest.mRouteType;
+            String requestedRouteFeature = matchingRequest.mRouteFeature;
 
             if (sessionInfo == null) {
                 // TODO: We may need to distinguish between failure and rejection.
                 //       One way can be introducing 'reason'.
-                notifySessionCreationFailed(requestedRoute, requestedRouteType);
+                notifySessionCreationFailed(requestedRoute, requestedRouteFeature);
                 return;
-            } 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()
+            } else if (!TextUtils.equals(requestedRouteFeature,
+                    sessionInfo.getRouteFeature())) {
+                Log.w(TAG, "The session has different route feature from what we requested. "
+                        + "(requested=" + requestedRouteFeature
+                        + ", actual=" + sessionInfo.getRouteFeature()
                         + ")");
-                notifySessionCreationFailed(requestedRoute, requestedRouteType);
+                notifySessionCreationFailed(requestedRoute, requestedRouteFeature);
                 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, requestedRouteType);
+                notifySessionCreationFailed(requestedRoute, requestedRouteFeature);
                 return;
             } else if (!TextUtils.equals(requestedRoute.getProviderId(),
                     sessionInfo.getProviderId())) {
@@ -532,29 +496,29 @@
                         + "(requested route's providerId=" + requestedRoute.getProviderId()
                         + ", actual providerId=" + sessionInfo.getProviderId()
                         + ")");
-                notifySessionCreationFailed(requestedRoute, requestedRouteType);
+                notifySessionCreationFailed(requestedRoute, requestedRouteFeature);
                 return;
             }
         }
 
         if (sessionInfo != null) {
-            RouteSessionController controller = new RouteSessionController(sessionInfo);
+            RoutingController controller = new RoutingController(sessionInfo);
             synchronized (sRouterLock) {
-                mSessionControllers.put(controller.getSessionId(), controller);
+                mRoutingControllers.put(controller.getSessionId(), controller);
             }
             notifySessionCreated(controller);
         }
     }
 
-    void changeSessionInfoOnHandler(RouteSessionInfo sessionInfo) {
+    void changeSessionInfoOnHandler(RoutingSessionInfo sessionInfo) {
         if (sessionInfo == null) {
             Log.w(TAG, "changeSessionInfoOnHandler: Ignoring null sessionInfo.");
             return;
         }
 
-        RouteSessionController matchingController;
+        RoutingController matchingController;
         synchronized (sRouterLock) {
-            matchingController = mSessionControllers.get(sessionInfo.getId());
+            matchingController = mRoutingControllers.get(sessionInfo.getId());
         }
 
         if (matchingController == null) {
@@ -563,27 +527,27 @@
             return;
         }
 
-        RouteSessionInfo oldInfo = matchingController.getRouteSessionInfo();
+        RoutingSessionInfo oldInfo = matchingController.getRoutingSessionInfo();
         if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) {
             Log.w(TAG, "changeSessionInfoOnHandler: Provider IDs are not matched. old="
                     + oldInfo.getProviderId() + ", new=" + sessionInfo.getProviderId());
             return;
         }
 
-        matchingController.setRouteSessionInfo(sessionInfo);
+        matchingController.setRoutingSessionInfo(sessionInfo);
         notifySessionInfoChanged(matchingController, oldInfo, sessionInfo);
     }
 
-    void releaseControllerOnHandler(RouteSessionInfo sessionInfo) {
+    void releaseControllerOnHandler(RoutingSessionInfo sessionInfo) {
         if (sessionInfo == null) {
             Log.w(TAG, "releaseControllerOnHandler: Ignoring null sessionInfo.");
             return;
         }
 
         final String uniqueSessionId = sessionInfo.getId();
-        RouteSessionController matchingController;
+        RoutingController matchingController;
         synchronized (sRouterLock) {
-            matchingController = mSessionControllers.get(uniqueSessionId);
+            matchingController = mRoutingControllers.get(uniqueSessionId);
         }
 
         if (matchingController == null) {
@@ -594,7 +558,7 @@
             return;
         }
 
-        RouteSessionInfo oldInfo = matchingController.getRouteSessionInfo();
+        RoutingSessionInfo oldInfo = matchingController.getRoutingSessionInfo();
         if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) {
             Log.w(TAG, "releaseControllerOnHandler: Provider IDs are not matched. old="
                     + oldInfo.getProviderId() + ", new=" + sessionInfo.getProviderId());
@@ -602,23 +566,23 @@
         }
 
         synchronized (sRouterLock) {
-            mSessionControllers.remove(uniqueSessionId, matchingController);
+            mRoutingControllers.remove(uniqueSessionId, matchingController);
         }
         matchingController.release();
         notifyControllerReleased(matchingController);
     }
 
     private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
-            RouteDiscoveryRequest discoveryRequest) {
+            RouteDiscoveryPreference discoveryRequest) {
         return routes.stream()
                 .filter(
-                        route -> route.containsRouteTypes(discoveryRequest.getRouteTypes()))
+                        route -> route.hasAnyFeatures(discoveryRequest.getPreferredFeatures()))
                 .collect(Collectors.toList());
     }
 
     private void notifyRoutesAdded(List<MediaRoute2Info> routes) {
         for (RouteCallbackRecord record: mRouteCallbackRecords) {
-            List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mRequest);
+            List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mPreference);
             if (!filteredRoutes.isEmpty()) {
                 record.mExecutor.execute(
                         () -> record.mRouteCallback.onRoutesAdded(filteredRoutes));
@@ -628,7 +592,7 @@
 
     private void notifyRoutesRemoved(List<MediaRoute2Info> routes) {
         for (RouteCallbackRecord record: mRouteCallbackRecords) {
-            List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mRequest);
+            List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mPreference);
             if (!filteredRoutes.isEmpty()) {
                 record.mExecutor.execute(
                         () -> record.mRouteCallback.onRoutesRemoved(filteredRoutes));
@@ -638,7 +602,7 @@
 
     private void notifyRoutesChanged(List<MediaRoute2Info> routes) {
         for (RouteCallbackRecord record: mRouteCallbackRecords) {
-            List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mRequest);
+            List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mPreference);
             if (!filteredRoutes.isEmpty()) {
                 record.mExecutor.execute(
                         () -> record.mRouteCallback.onRoutesChanged(filteredRoutes));
@@ -646,22 +610,22 @@
         }
     }
 
-    private void notifySessionCreated(RouteSessionController controller) {
+    private void notifySessionCreated(RoutingController controller) {
         for (SessionCallbackRecord record: mSessionCallbackRecords) {
             record.mExecutor.execute(
                     () -> record.mSessionCallback.onSessionCreated(controller));
         }
     }
 
-    private void notifySessionCreationFailed(MediaRoute2Info route, String routeType) {
+    private void notifySessionCreationFailed(MediaRoute2Info route, String routeFeature) {
         for (SessionCallbackRecord record: mSessionCallbackRecords) {
             record.mExecutor.execute(
-                    () -> record.mSessionCallback.onSessionCreationFailed(route, routeType));
+                    () -> record.mSessionCallback.onSessionCreationFailed(route, routeFeature));
         }
     }
 
-    private void notifySessionInfoChanged(RouteSessionController controller,
-            RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
+    private void notifySessionInfoChanged(RoutingController controller,
+            RoutingSessionInfo oldInfo, RoutingSessionInfo newInfo) {
         for (SessionCallbackRecord record: mSessionCallbackRecords) {
             record.mExecutor.execute(
                     () -> record.mSessionCallback.onSessionInfoChanged(
@@ -669,7 +633,7 @@
         }
     }
 
-    private void notifyControllerReleased(RouteSessionController controller) {
+    private void notifyControllerReleased(RoutingController controller) {
         for (SessionCallbackRecord record: mSessionCallbackRecords) {
             record.mExecutor.execute(
                     () -> record.mSessionCallback.onSessionReleased(controller));
@@ -710,23 +674,24 @@
 
     /**
      * Callback for receiving a result of session creation and session updates.
+     * @hide
      */
     public static class SessionCallback {
         /**
-         * Called when the route session is created by the route provider.
+         * Called when the routing session is created by the route provider.
          *
          * @param controller the controller to control the created session
          */
-        public void onSessionCreated(@NonNull RouteSessionController controller) {}
+        public void onSessionCreated(@NonNull RoutingController controller) {}
 
         /**
          * Called when the session creation request failed.
          *
          * @param requestedRoute the route info which was used for the request
-         * @param requestedRouteType the route type which was used for the request
+         * @param requestedRouteFeature the route feature which was used for the request
          */
         public void onSessionCreationFailed(@NonNull MediaRoute2Info requestedRoute,
-                @NonNull String requestedRouteType) {}
+                @NonNull String requestedRouteFeature) {}
 
         /**
          * Called when the session info has changed.
@@ -737,44 +702,45 @@
          * TODO: (Discussion) Do we really need newInfo? The controller has the newInfo.
          *       However. there can be timing issue if there is no newInfo.
          */
-        public void onSessionInfoChanged(@NonNull RouteSessionController controller,
-                @NonNull RouteSessionInfo oldInfo,
-                @NonNull RouteSessionInfo newInfo) {}
+        public void onSessionInfoChanged(@NonNull RoutingController controller,
+                @NonNull RoutingSessionInfo oldInfo,
+                @NonNull RoutingSessionInfo newInfo) {}
 
         /**
          * Called when the session is released by {@link MediaRoute2ProviderService}.
          * Before this method is called, the controller would be released by the system,
-         * which means the {@link RouteSessionController#isReleased()} will always return true
+         * which means the {@link RoutingController#isReleased()} will always return true
          * for the {@code controller} here.
          * <p>
-         * Note: Calling {@link RouteSessionController#release()} will <em>NOT</em> trigger
+         * Note: Calling {@link RoutingController#release()} will <em>NOT</em> trigger
          * this method to be called.
          *
          * TODO: Add tests for checking whether this method is called.
          * TODO: When service process dies, this should be called.
          *
-         * @see RouteSessionController#isReleased()
+         * @see RoutingController#isReleased()
          */
-        public void onSessionReleased(@NonNull RouteSessionController controller) {}
+        public void onSessionReleased(@NonNull RoutingController controller) {}
     }
 
     /**
-     * A class to control media route session in media route provider.
+     * A class to control media routing session in media route provider.
      * For example, selecting/deselcting/transferring routes to session can be done through this
      * class. Instances are created by {@link MediaRouter2}.
      *
      * TODO: Need to add toString()
+     * @hide
      */
-    public final class RouteSessionController {
+    public final class RoutingController {
         private final Object mControllerLock = new Object();
 
         @GuardedBy("mLock")
-        private RouteSessionInfo mSessionInfo;
+        private RoutingSessionInfo mSessionInfo;
 
         @GuardedBy("mLock")
         private volatile boolean mIsReleased;
 
-        RouteSessionController(@NonNull RouteSessionInfo sessionInfo) {
+        RoutingController(@NonNull RoutingSessionInfo sessionInfo) {
             mSessionInfo = sessionInfo;
         }
 
@@ -788,17 +754,17 @@
         }
 
         /**
-         * @return the type of routes that the session includes.
+         * @return the feature which is used by the session mainly.
          */
         @NonNull
-        public String getRouteType() {
+        public String getRouteFeature() {
             synchronized (mControllerLock) {
-                return mSessionInfo.getRouteType();
+                return mSessionInfo.getRouteFeature();
             }
         }
 
         /**
-         * @return the control hints used to control route session if available.
+         * @return the control hints used to control routing session if available.
          */
         @Nullable
         public Bundle getControlHints() {
@@ -1020,7 +986,7 @@
 
             Client2 client;
             synchronized (sRouterLock) {
-                mSessionControllers.remove(getSessionId(), this);
+                mRoutingControllers.remove(getSessionId(), this);
                 client = mClient;
             }
             if (client != null) {
@@ -1037,13 +1003,13 @@
          * @hide
          */
         @NonNull
-        public RouteSessionInfo getRouteSessionInfo() {
+        public RoutingSessionInfo getRoutingSessionInfo() {
             synchronized (mControllerLock) {
                 return mSessionInfo;
             }
         }
 
-        void setRouteSessionInfo(@NonNull RouteSessionInfo info) {
+        void setRoutingSessionInfo(@NonNull RoutingSessionInfo info) {
             synchronized (mControllerLock) {
                 mSessionInfo = info;
             }
@@ -1070,13 +1036,13 @@
     final class RouteCallbackRecord {
         public final Executor mExecutor;
         public final RouteCallback mRouteCallback;
-        public final RouteDiscoveryRequest mRequest;
+        public final RouteDiscoveryPreference mPreference;
 
         RouteCallbackRecord(@Nullable Executor executor, @NonNull RouteCallback routeCallback,
-                @Nullable RouteDiscoveryRequest request) {
+                @Nullable RouteDiscoveryPreference preference) {
             mRouteCallback = routeCallback;
             mExecutor = executor;
-            mRequest = request;
+            mPreference = preference;
         }
 
         @Override
@@ -1125,13 +1091,13 @@
 
     final class SessionCreationRequest {
         public final MediaRoute2Info mRoute;
-        public final String mRouteType;
+        public final String mRouteFeature;
         public final int mRequestId;
 
         SessionCreationRequest(int requestId, @NonNull MediaRoute2Info route,
-                @NonNull String routeType) {
+                @NonNull String routeFeature) {
             mRoute = route;
-            mRouteType = routeType;
+            mRouteFeature = routeFeature;
             mRequestId = requestId;
         }
     }
@@ -1159,19 +1125,19 @@
         }
 
         @Override
-        public void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, int requestId) {
+        public void notifySessionCreated(@Nullable RoutingSessionInfo sessionInfo, int requestId) {
             mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
                     MediaRouter2.this, sessionInfo, requestId));
         }
 
         @Override
-        public void notifySessionInfoChanged(@Nullable RouteSessionInfo sessionInfo) {
+        public void notifySessionInfoChanged(@Nullable RoutingSessionInfo sessionInfo) {
             mHandler.sendMessage(obtainMessage(MediaRouter2::changeSessionInfoOnHandler,
                     MediaRouter2.this, sessionInfo));
         }
 
         @Override
-        public void notifySessionReleased(RouteSessionInfo sessionInfo) {
+        public void notifySessionReleased(RoutingSessionInfo sessionInfo) {
             mHandler.sendMessage(obtainMessage(MediaRouter2::releaseControllerOnHandler,
                     MediaRouter2.this, sessionInfo));
         }
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 1e6ec51..7022933 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>> mRouteTypeMap = new ConcurrentHashMap<>();
+    final ConcurrentMap<String, List<String>> mPreferredFeaturesMap = new ConcurrentHashMap<>();
 
     private AtomicInteger mNextRequestId = new AtomicInteger(1);
 
@@ -144,7 +144,7 @@
                 }
                 //TODO: clear mRoutes?
                 mClient = null;
-                mRouteTypeMap.clear();
+                mPreferredFeaturesMap.clear();
             }
         }
     }
@@ -160,14 +160,14 @@
     public List<MediaRoute2Info> getAvailableRoutes(@NonNull String packageName) {
         Objects.requireNonNull(packageName, "packageName must not be null");
 
-        List<String> routeTypes = mRouteTypeMap.get(packageName);
-        if (routeTypes == null) {
+        List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
+        if (preferredFeatures == null) {
             return Collections.emptyList();
         }
         List<MediaRoute2Info> routes = new ArrayList<>();
         synchronized (mRoutesLock) {
             for (MediaRoute2Info route : mRoutes.values()) {
-                if (route.containsRouteTypes(routeTypes)) {
+                if (route.hasAnyFeatures(preferredFeatures)) {
                     routes.add(route);
                 }
             }
@@ -176,7 +176,7 @@
     }
 
     @NonNull
-    public List<RouteSessionInfo> getActiveSessions() {
+    public List<RoutingSessionInfo> getActiveSessions() {
         Client client;
         synchronized (sLock) {
             client = mClient;
@@ -352,15 +352,15 @@
         }
     }
 
-    void updateRouteTypes(String packageName, List<String> routeTypes) {
-        List<String> prevTypes = mRouteTypeMap.put(packageName, routeTypes);
-        if ((prevTypes == null && routeTypes.size() == 0)
-                || Objects.equals(routeTypes, prevTypes)) {
+    void updatePreferredFeatures(String packageName, List<String> preferredFeatures) {
+        List<String> prevFeatures = mPreferredFeaturesMap.put(packageName, preferredFeatures);
+        if ((prevFeatures == null && preferredFeatures.size() == 0)
+                || Objects.equals(preferredFeatures, prevFeatures)) {
             return;
         }
         for (CallbackRecord record : mCallbackRecords) {
-            record.mExecutor.execute(
-                    () -> record.mCallback.onControlCategoriesChanged(packageName, routeTypes));
+            record.mExecutor.execute(() -> record.mCallback
+                    .onControlCategoriesChanged(packageName, preferredFeatures));
         }
     }
 
@@ -398,13 +398,13 @@
 
 
         /**
-         * Called when the route types of an app is changed.
+         * Called when the preferred route features of an app is changed.
          *
          * @param packageName the package name of the application
-         * @param routeTypes the list of route types set by an application.
+         * @param preferredFeatures the list of preferred route features set by an application.
          */
         public void onControlCategoriesChanged(@NonNull String packageName,
-                @NonNull List<String> routeTypes) {}
+                @NonNull List<String> preferredFeatures) {}
     }
 
     final class CallbackRecord {
@@ -440,9 +440,9 @@
                     MediaRouter2Manager.this, packageName, route));
         }
 
-        public void notifyRouteTypesChanged(String packageName, List<String> routeTypes) {
-            mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updateRouteTypes,
-                    MediaRouter2Manager.this, packageName, routeTypes));
+        public void notifyPreferredFeaturesChanged(String packageName, List<String> features) {
+            mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updatePreferredFeatures,
+                    MediaRouter2Manager.this, packageName, features));
         }
 
         @Override
diff --git a/media/java/android/media/RouteDiscoveryRequest.aidl b/media/java/android/media/RouteDiscoveryPreference.aidl
similarity index 94%
rename from media/java/android/media/RouteDiscoveryRequest.aidl
rename to media/java/android/media/RouteDiscoveryPreference.aidl
index 744f656..898eb39 100644
--- a/media/java/android/media/RouteDiscoveryRequest.aidl
+++ b/media/java/android/media/RouteDiscoveryPreference.aidl
@@ -16,4 +16,4 @@
 
 package android.media;
 
-parcelable RouteDiscoveryRequest;
+parcelable RouteDiscoveryPreference;
diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java
new file mode 100644
index 0000000..7ec1123
--- /dev/null
+++ b/media/java/android/media/RouteDiscoveryPreference.java
@@ -0,0 +1,215 @@
+/*
+ * 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;
+
+/**
+ * A media route discovery preference  describing the kinds of routes that media router
+ * would like to discover and whether to perform active scanning.
+ *
+ * @see MediaRouter2#registerRouteCallback
+ */
+public final class RouteDiscoveryPreference implements Parcelable {
+    @NonNull
+    public static final Creator<RouteDiscoveryPreference> CREATOR =
+            new Creator<RouteDiscoveryPreference>() {
+                @Override
+                public RouteDiscoveryPreference createFromParcel(Parcel in) {
+                    return new RouteDiscoveryPreference(in);
+                }
+
+                @Override
+                public RouteDiscoveryPreference[] newArray(int size) {
+                    return new RouteDiscoveryPreference[size];
+                }
+            };
+
+    @NonNull
+    private final List<String> mPreferredFeatures;
+    private final boolean mActiveScan;
+    @Nullable
+    private final Bundle mExtras;
+
+    /**
+     * @hide
+     */
+    public static final RouteDiscoveryPreference EMPTY =
+            new Builder(Collections.emptyList(), false).build();
+
+    RouteDiscoveryPreference(@NonNull Builder builder) {
+        mPreferredFeatures = builder.mPreferredFeatures;
+        mActiveScan = builder.mActiveScan;
+        mExtras = builder.mExtras;
+    }
+
+    RouteDiscoveryPreference(@NonNull Parcel in) {
+        mPreferredFeatures = in.createStringArrayList();
+        mActiveScan = in.readBoolean();
+        mExtras = in.readBundle();
+    }
+
+    @NonNull
+    public List<String> getPreferredFeatures() {
+        return mPreferredFeatures;
+    }
+
+    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(mPreferredFeatures);
+        dest.writeBoolean(mActiveScan);
+        dest.writeBundle(mExtras);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder()
+                .append("RouteDiscoveryRequest{ ")
+                .append("preferredFeatures={")
+                .append(String.join(", ", mPreferredFeatures))
+                .append("}")
+                .append(", activeScan=")
+                .append(mActiveScan)
+                .append(" }");
+
+        return result.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof RouteDiscoveryPreference)) {
+            return false;
+        }
+        RouteDiscoveryPreference other = (RouteDiscoveryPreference) o;
+        return Objects.equals(mPreferredFeatures, other.mPreferredFeatures)
+                && mActiveScan == other.mActiveScan;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPreferredFeatures, mActiveScan);
+    }
+
+    /**
+     * Builder for {@link RouteDiscoveryPreference}.
+     */
+    public static final class Builder {
+        List<String> mPreferredFeatures;
+        boolean mActiveScan;
+        Bundle mExtras;
+
+        public Builder(@NonNull List<String> preferredFeatures, boolean activeScan) {
+            mPreferredFeatures = new ArrayList<>(Objects.requireNonNull(preferredFeatures,
+                    "preferredFeatures must not be null"));
+            mActiveScan = activeScan;
+        }
+
+        public Builder(@NonNull RouteDiscoveryPreference preference) {
+            Objects.requireNonNull(preference, "preference must not be null");
+
+            mPreferredFeatures = preference.getPreferredFeatures();
+            mActiveScan = preference.isActiveScan();
+            mExtras = preference.getExtras();
+        }
+
+        /**
+         * A constructor to combine all of the preferences into a single preference .
+         * It ignores extras of preferences.
+         *
+         * @hide
+         */
+        public Builder(@NonNull Collection<RouteDiscoveryPreference> preferences) {
+            Objects.requireNonNull(preferences, "preferences must not be null");
+
+            Set<String> routeFeatureSet = new HashSet<>();
+            mActiveScan = false;
+            for (RouteDiscoveryPreference preference : preferences) {
+                routeFeatureSet.addAll(preference.mPreferredFeatures);
+                mActiveScan |= preference.mActiveScan;
+            }
+            mPreferredFeatures = new ArrayList<>(routeFeatureSet);
+        }
+
+        /**
+         * Sets preferred route features to discover.
+         */
+        @NonNull
+        public Builder setPreferredFeatures(@NonNull List<String> preferredFeatures) {
+            mPreferredFeatures = new ArrayList<>(Objects.requireNonNull(preferredFeatures,
+                            "preferredFeatures must not be null"));
+            return this;
+        }
+
+        /**
+         * Sets if active scanning should be performed.
+         */
+        @NonNull
+        public Builder setActiveScan(boolean activeScan) {
+            mActiveScan = activeScan;
+            return this;
+        }
+
+        /**
+         * Sets the extras of the route.
+         * @hide
+         */
+        @NonNull
+        public Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds the {@link RouteDiscoveryPreference}.
+         */
+        @NonNull
+        public RouteDiscoveryPreference build() {
+            return new RouteDiscoveryPreference(this);
+        }
+    }
+}
diff --git a/media/java/android/media/RouteDiscoveryRequest.java b/media/java/android/media/RouteDiscoveryRequest.java
deleted file mode 100644
index 88b31fb..0000000
--- a/media/java/android/media/RouteDiscoveryRequest.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * 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.aidl b/media/java/android/media/RoutingSessionInfo.aidl
similarity index 95%
rename from media/java/android/media/RouteSessionInfo.aidl
rename to media/java/android/media/RoutingSessionInfo.aidl
index fb5d836..7b8e3d9 100644
--- a/media/java/android/media/RouteSessionInfo.aidl
+++ b/media/java/android/media/RoutingSessionInfo.aidl
@@ -16,4 +16,4 @@
 
 package android.media;
 
-parcelable RouteSessionInfo;
+parcelable RoutingSessionInfo;
diff --git a/media/java/android/media/RouteSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
similarity index 65%
rename from media/java/android/media/RouteSessionInfo.java
rename to media/java/android/media/RoutingSessionInfo.java
index 5330630..96acf6c 100644
--- a/media/java/android/media/RouteSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -30,29 +30,28 @@
 import java.util.Objects;
 
 /**
- * Describes a route session that is made when a media route is selected.
+ * Describes a routing session which is created when a media route is selected.
  * @hide
  */
-public class RouteSessionInfo implements Parcelable {
-
+public final class RoutingSessionInfo implements Parcelable {
     @NonNull
-    public static final Creator<RouteSessionInfo> CREATOR =
-            new Creator<RouteSessionInfo>() {
+    public static final Creator<RoutingSessionInfo> CREATOR =
+            new Creator<RoutingSessionInfo>() {
                 @Override
-                public RouteSessionInfo createFromParcel(Parcel in) {
-                    return new RouteSessionInfo(in);
+                public RoutingSessionInfo createFromParcel(Parcel in) {
+                    return new RoutingSessionInfo(in);
                 }
                 @Override
-                public RouteSessionInfo[] newArray(int size) {
-                    return new RouteSessionInfo[size];
+                public RoutingSessionInfo[] newArray(int size) {
+                    return new RoutingSessionInfo[size];
                 }
             };
 
-    public static final String TAG = "RouteSessionInfo";
+    private static final String TAG = "RoutingSessionInfo";
 
     final String mId;
     final String mClientPackageName;
-    final String mRouteType;
+    final String mRouteFeature;
     @Nullable
     final String mProviderId;
     final List<String> mSelectedRoutes;
@@ -62,14 +61,15 @@
     @Nullable
     final Bundle mControlHints;
 
-    RouteSessionInfo(@NonNull Builder builder) {
+    RoutingSessionInfo(@NonNull Builder builder) {
         Objects.requireNonNull(builder, "builder must not be null.");
 
         mId = builder.mId;
         mClientPackageName = builder.mClientPackageName;
-        mRouteType = builder.mRouteType;
+        mRouteFeature = builder.mRouteFeature;
         mProviderId = builder.mProviderId;
 
+        // TODO: Needs to check that the routes already have unique IDs.
         mSelectedRoutes = Collections.unmodifiableList(
                 convertToUniqueRouteIds(builder.mSelectedRoutes));
         mSelectableRoutes = Collections.unmodifiableList(
@@ -82,12 +82,12 @@
         mControlHints = builder.mControlHints;
     }
 
-    RouteSessionInfo(@NonNull Parcel src) {
+    RoutingSessionInfo(@NonNull Parcel src) {
         Objects.requireNonNull(src, "src must not be null.");
 
         mId = ensureString(src.readString());
         mClientPackageName = ensureString(src.readString());
-        mRouteType = ensureString(src.readString());
+        mRouteFeature = ensureString(src.readString());
         mProviderId = src.readString();
 
         mSelectedRoutes = ensureList(src.createStringArrayList());
@@ -113,18 +113,6 @@
     }
 
     /**
-     * Returns whether the session info is valid or not
-     *
-     * TODO in this CL: Remove this method.
-     */
-    public boolean isValid() {
-        return !TextUtils.isEmpty(mId)
-                && !TextUtils.isEmpty(mClientPackageName)
-                && !TextUtils.isEmpty(mRouteType)
-                && mSelectedRoutes.size() > 0;
-    }
-
-    /**
      * Gets the id of the session. The sessions which are given by {@link MediaRouter2} will have
      * unique IDs.
      * <p>
@@ -160,12 +148,12 @@
     }
 
     /**
-     * Gets the route type of the session.
-     * Routes that don't have the type can't be added to the session.
+     * Gets the route feature of the session.
+     * Routes that don't have the feature can't be selected into the session.
      */
     @NonNull
-    public String getRouteType() {
-        return mRouteType;
+    public String getRouteFeature() {
+        return mRouteFeature;
     }
 
     /**
@@ -226,7 +214,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mId);
         dest.writeString(mClientPackageName);
-        dest.writeString(mRouteType);
+        dest.writeString(mRouteFeature);
         dest.writeString(mProviderId);
         dest.writeStringList(mSelectedRoutes);
         dest.writeStringList(mSelectableRoutes);
@@ -236,11 +224,37 @@
     }
 
     @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof RoutingSessionInfo)) {
+            return false;
+        }
+
+        RoutingSessionInfo other = (RoutingSessionInfo) obj;
+        return Objects.equals(mId, other.mId)
+                && Objects.equals(mClientPackageName, other.mClientPackageName)
+                && Objects.equals(mRouteFeature, other.mRouteFeature)
+                && Objects.equals(mProviderId, other.mProviderId)
+                && Objects.equals(mSelectedRoutes, other.mSelectedRoutes)
+                && Objects.equals(mSelectableRoutes, other.mSelectableRoutes)
+                && Objects.equals(mDeselectableRoutes, other.mDeselectableRoutes)
+                && Objects.equals(mTransferrableRoutes, other.mTransferrableRoutes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mId, mClientPackageName, mRouteFeature, mProviderId,
+                mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferrableRoutes);
+    }
+
+    @Override
     public String toString() {
         StringBuilder result = new StringBuilder()
-                .append("RouteSessionInfo{ ")
+                .append("RoutingSessionInfo{ ")
                 .append("sessionId=").append(mId)
-                .append(", routeType=").append(mRouteType)
+                .append(", routeFeature=").append(mRouteFeature)
                 .append(", selectedRoutes={")
                 .append(String.join(",", mSelectedRoutes))
                 .append("}")
@@ -276,12 +290,12 @@
     }
 
     /**
-     * Builder class for {@link RouteSessionInfo}.
+     * Builder class for {@link RoutingSessionInfo}.
      */
     public static final class Builder {
         final String mId;
         final String mClientPackageName;
-        final String mRouteType;
+        final String mRouteFeature;
         String mProviderId;
         final List<String> mSelectedRoutes;
         final List<String> mSelectableRoutes;
@@ -290,24 +304,32 @@
         Bundle mControlHints;
 
         /**
-         * Constructor for builder to create {@link RouteSessionInfo}.
+         * Constructor for builder to create {@link RoutingSessionInfo}.
          * <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 RoutingSessionInfo#getId()} can be different from what was set in
          * {@link MediaRoute2ProviderService}.
          * </p>
          *
+         * @param id ID of the session. Must not be empty.
+         * @param clientPackageName package name of the client app which uses this session.
+         *                          If is is unknown, then just use an empty string.
+         * @param routeFeature the route feature of session. Must not be empty.
          * @see MediaRoute2Info#getId()
          */
         public Builder(@NonNull String id, @NonNull String clientPackageName,
-                @NonNull String routeType) {
+                @NonNull String routeFeature) {
             if (TextUtils.isEmpty(id)) {
                 throw new IllegalArgumentException("id must not be empty");
             }
+            Objects.requireNonNull(clientPackageName, "clientPackageName must not be null");
+            if (TextUtils.isEmpty(routeFeature)) {
+                throw new IllegalArgumentException("routeFeature must not be empty");
+            }
+
             mId = id;
-            mClientPackageName = Objects.requireNonNull(
-                    clientPackageName, "clientPackageName must not be null");
-            mRouteType = Objects.requireNonNull(routeType, "routeType must not be null");
+            mClientPackageName = clientPackageName;
+            mRouteFeature = routeFeature;
             mSelectedRoutes = new ArrayList<>();
             mSelectableRoutes = new ArrayList<>();
             mDeselectableRoutes = new ArrayList<>();
@@ -315,17 +337,17 @@
         }
 
         /**
-         * Constructor for builder to create {@link RouteSessionInfo} with
-         * existing {@link RouteSessionInfo} instance.
+         * Constructor for builder to create {@link RoutingSessionInfo} with
+         * existing {@link RoutingSessionInfo} instance.
          *
          * @param sessionInfo the existing instance to copy data from.
          */
-        public Builder(@NonNull RouteSessionInfo sessionInfo) {
+        public Builder(@NonNull RoutingSessionInfo sessionInfo) {
             Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
 
             mId = sessionInfo.mId;
             mClientPackageName = sessionInfo.mClientPackageName;
-            mRouteType = sessionInfo.mRouteType;
+            mRouteFeature = sessionInfo.mRouteFeature;
             mProviderId = sessionInfo.mProviderId;
 
             mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes);
@@ -338,8 +360,6 @@
 
         /**
          * 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
          */
@@ -362,20 +382,26 @@
         }
 
         /**
-         * Adds a route to the selected routes.
+         * Adds a route to the selected routes. The {@code routeId} must not be empty.
          */
         @NonNull
         public Builder addSelectedRoute(@NonNull String routeId) {
-            mSelectedRoutes.add(Objects.requireNonNull(routeId, "routeId must not be null"));
+            if (TextUtils.isEmpty(routeId)) {
+                throw new IllegalArgumentException("routeId must not be empty");
+            }
+            mSelectedRoutes.add(routeId);
             return this;
         }
 
         /**
-         * Removes a route from the selected routes.
+         * Removes a route from the selected routes. The {@code routeId} must not be empty.
          */
         @NonNull
         public Builder removeSelectedRoute(@NonNull String routeId) {
-            mSelectedRoutes.remove(Objects.requireNonNull(routeId, "routeId must not be null"));
+            if (TextUtils.isEmpty(routeId)) {
+                throw new IllegalArgumentException("routeId must not be empty");
+            }
+            mSelectedRoutes.remove(routeId);
             return this;
         }
 
@@ -389,20 +415,26 @@
         }
 
         /**
-         * Adds a route to the selectable routes.
+         * Adds a route to the selectable routes. The {@code routeId} must not be empty.
          */
         @NonNull
         public Builder addSelectableRoute(@NonNull String routeId) {
-            mSelectableRoutes.add(Objects.requireNonNull(routeId, "routeId must not be null"));
+            if (TextUtils.isEmpty(routeId)) {
+                throw new IllegalArgumentException("routeId must not be empty");
+            }
+            mSelectableRoutes.add(routeId);
             return this;
         }
 
         /**
-         * Removes a route from the selectable routes.
+         * Removes a route from the selectable routes. The {@code routeId} must not be empty.
          */
         @NonNull
         public Builder removeSelectableRoute(@NonNull String routeId) {
-            mSelectableRoutes.remove(Objects.requireNonNull(routeId, "routeId must not be null"));
+            if (TextUtils.isEmpty(routeId)) {
+                throw new IllegalArgumentException("routeId must not be empty");
+            }
+            mSelectableRoutes.remove(routeId);
             return this;
         }
 
@@ -416,20 +448,26 @@
         }
 
         /**
-         * Adds a route to the deselectable routes.
+         * Adds a route to the deselectable routes. The {@code routeId} must not be empty.
          */
         @NonNull
         public Builder addDeselectableRoute(@NonNull String routeId) {
-            mDeselectableRoutes.add(Objects.requireNonNull(routeId, "routeId must not be null"));
+            if (TextUtils.isEmpty(routeId)) {
+                throw new IllegalArgumentException("routeId must not be empty");
+            }
+            mDeselectableRoutes.add(routeId);
             return this;
         }
 
         /**
-         * Removes a route from the deselectable routes.
+         * Removes a route from the deselectable routes. The {@code routeId} must not be empty.
          */
         @NonNull
         public Builder removeDeselectableRoute(@NonNull String routeId) {
-            mDeselectableRoutes.remove(Objects.requireNonNull(routeId, "routeId must not be null"));
+            if (TextUtils.isEmpty(routeId)) {
+                throw new IllegalArgumentException("routeId must not be empty");
+            }
+            mDeselectableRoutes.remove(routeId);
             return this;
         }
 
@@ -443,21 +481,26 @@
         }
 
         /**
-         * Adds a route to the transferrable routes.
+         * Adds a route to the transferrable routes. The {@code routeId} must not be empty.
          */
         @NonNull
         public Builder addTransferrableRoute(@NonNull String routeId) {
-            mTransferrableRoutes.add(Objects.requireNonNull(routeId, "routeId must not be null"));
+            if (TextUtils.isEmpty(routeId)) {
+                throw new IllegalArgumentException("routeId must not be empty");
+            }
+            mTransferrableRoutes.add(routeId);
             return this;
         }
 
         /**
-         * Removes a route from the transferrable routes.
+         * Removes a route from the transferrable routes. The {@code routeId} must not be empty.
          */
         @NonNull
         public Builder removeTransferrableRoute(@NonNull String routeId) {
-            mTransferrableRoutes.remove(
-                    Objects.requireNonNull(routeId, "routeId must not be null"));
+            if (TextUtils.isEmpty(routeId)) {
+                throw new IllegalArgumentException("routeId must not be empty");
+            }
+            mTransferrableRoutes.remove(routeId);
             return this;
         }
 
@@ -471,11 +514,16 @@
         }
 
         /**
-         * Builds a route session info.
+         * Builds a routing session info.
+         *
+         * @throws IllegalArgumentException if no selected routes are added.
          */
         @NonNull
-        public RouteSessionInfo build() {
-            return new RouteSessionInfo(this);
+        public RoutingSessionInfo build() {
+            if (mSelectedRoutes.isEmpty()) {
+                throw new IllegalArgumentException("selectedRoutes must not be empty");
+            }
+            return new RoutingSessionInfo(this);
         }
     }
 }
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
index 77596a5..118f65c 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
@@ -25,6 +25,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
 import android.hardware.soundtrigger.SoundTrigger;
+import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
 import android.media.AudioFormat;
 import android.os.Handler;
@@ -75,7 +76,9 @@
             value = {
                 RECOGNITION_FLAG_NONE,
                 RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO,
-                RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS
+                RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS,
+                RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION,
+                    RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION,
             })
     public @interface RecognitionFlags {}
 
@@ -100,11 +103,34 @@
      * triggers after a call to {@link #startRecognition(int)}, if the model
      * triggers multiple times.
      * When this isn't specified, the default behavior is to stop recognition once the
-     * trigger happenss, till the caller starts recognition again.
+     * trigger happens, till the caller starts recognition again.
      */
     public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 0x2;
 
     /**
+     * Audio capabilities flag for {@link #startRecognition(int)} that indicates
+     * if the underlying recognition should use AEC.
+     * This capability may or may not be supported by the system, and support can be queried
+     * by calling {@link SoundTriggerManager#getModuleProperties()} and checking
+     * {@link ModuleProperties#audioCapabilities}. The corresponding capabilities field for
+     * this flag is {@link SoundTrigger.ModuleProperties#CAPABILITY_ECHO_CANCELLATION}.
+     * If this flag is passed without the audio capability supported, there will be no audio effect
+     * applied.
+     */
+    public static final int RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION = 0x4;
+
+    /**
+     * Audio capabilities flag for {@link #startRecognition(int)} that indicates
+     * if the underlying recognition should use noise suppression.
+     * This capability may or may not be supported by the system, and support can be queried
+     * by calling {@link SoundTriggerManager#getModuleProperties()} and checking
+     * {@link ModuleProperties#audioCapabilities}. The corresponding capabilities field for
+     * this flag is {@link SoundTrigger.ModuleProperties#CAPABILITY_NOISE_SUPPRESSION}. If this flag
+     * is passed without the audio capability supported, there will be no audio effect applied.
+     */
+    public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 0x8;
+
+    /**
      * Additional payload for {@link Callback#onDetected}.
      */
     public static class EventPayload {
@@ -267,11 +293,20 @@
 
         boolean allowMultipleTriggers =
                 (recognitionFlags & RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS) != 0;
-        int status = STATUS_OK;
+
+        int audioCapabilities = 0;
+        if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION) != 0) {
+            audioCapabilities |= SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION;
+        }
+        if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION) != 0) {
+            audioCapabilities |= SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION;
+        }
+
+        int status;
         try {
             status = mSoundTriggerService.startRecognition(new ParcelUuid(mSoundModelId),
                     mRecognitionCallback, new RecognitionConfig(captureTriggerAudio,
-                        allowMultipleTriggers, null, null));
+                        allowMultipleTriggers, null, null, audioCapabilities));
         } catch (RemoteException e) {
             return false;
         }
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 938ffcd..dd4dac2 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -44,6 +44,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.HashMap;
+import java.util.Objects;
 import java.util.UUID;
 
 /**
@@ -175,19 +176,40 @@
          * Factory constructor to create a SoundModel instance for use with methods in this
          * class.
          */
-        public static Model create(UUID modelUuid, UUID vendorUuid, byte[] data) {
-            return new Model(new SoundTrigger.GenericSoundModel(modelUuid,
-                        vendorUuid, data));
+        @NonNull
+        public static Model create(@NonNull UUID modelUuid, @NonNull UUID vendorUuid,
+                @Nullable byte[] data, int version) {
+            Objects.requireNonNull(modelUuid);
+            Objects.requireNonNull(vendorUuid);
+            return new Model(new SoundTrigger.GenericSoundModel(modelUuid, vendorUuid, data,
+                    version));
         }
 
+        /**
+         * Factory constructor to create a SoundModel instance for use with methods in this
+         * class.
+         */
+        @NonNull
+        public static Model create(@NonNull UUID modelUuid, @NonNull UUID vendorUuid,
+                @Nullable byte[] data) {
+            return create(modelUuid, vendorUuid, data, -1);
+        }
+
+        @NonNull
         public UUID getModelUuid() {
             return mGenericSoundModel.uuid;
         }
 
+        @NonNull
         public UUID getVendorUuid() {
             return mGenericSoundModel.vendorUuid;
         }
 
+        public int getVersion() {
+            return mGenericSoundModel.version;
+        }
+
+        @Nullable
         public byte[] getModelData() {
             return mGenericSoundModel.data;
         }
diff --git a/media/java/android/media/soundtrigger_middleware/AudioCapabilities.aidl b/media/java/android/media/soundtrigger_middleware/AudioCapabilities.aidl
new file mode 100644
index 0000000..97a8849
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/AudioCapabilities.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+/**
+ * AudioCapabilities supported by the implemented HAL driver.
+ * @hide
+ */
+@Backing(type="int")
+enum AudioCapabilities {
+    /**
+     * If set the underlying module supports AEC.
+     */
+    ECHO_CANCELLATION = 1 << 0,
+    /**
+     * If set, the underlying module supports noise suppression.
+     */
+    NOISE_SUPPRESSION = 1 << 1,
+}
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl b/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl
index c7642e8..5c0eeb1 100644
--- a/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl
+++ b/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl
@@ -28,6 +28,12 @@
     /* Configuration for each key phrase. */
     PhraseRecognitionExtra[] phraseRecognitionExtras;
 
+    /**
+     * Bit field encoding of the AudioCapabilities
+     * supported by the firmware.
+     */
+    int audioCapabilities;
+
     /** Opaque capture configuration data. */
     byte[] data;
 }
diff --git a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl b/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
index 909f1a00..9c56e7b 100644
--- a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
+++ b/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
@@ -58,4 +58,11 @@
      * Rated power consumption when detection is active with TDB
      * silence/sound/speech ratio */
     int powerConsumptionMw;
+    /**
+     * Bit field encoding of the AudioCapabilities
+     * supported by the firmware.
+     * This property is supported for soundtrigger HAL v2.3 and above.
+     * If running a previous version, this value will be 0.
+     */
+    int audioCapabilities;
 }
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index b076bb6..b5e9d1b 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -60,6 +60,7 @@
     void createSession(in ITvInputClient client, in String inputId, boolean isRecordingSession,
             int seq, int userId);
     void releaseSession(in IBinder sessionToken, int userId);
+    int getClientPid(in String sessionId);
 
     void setMainSession(in IBinder sessionToken, int userId);
     void setSurface(in IBinder sessionToken, in Surface surface, int userId);
diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
index f90c504..8ccf13a 100755
--- a/media/java/android/media/tv/ITvInputService.aidl
+++ b/media/java/android/media/tv/ITvInputService.aidl
@@ -30,8 +30,9 @@
     void registerCallback(in ITvInputServiceCallback callback);
     void unregisterCallback(in ITvInputServiceCallback callback);
     void createSession(in InputChannel channel, in ITvInputSessionCallback callback,
-            in String inputId);
-    void createRecordingSession(in ITvInputSessionCallback callback, in String inputId);
+            in String inputId, in String sessionId);
+    void createRecordingSession(in ITvInputSessionCallback callback, in String inputId,
+            in String sessionId);
 
     // For hardware TvInputService
     void notifyHardwareAdded(in TvInputHardwareInfo hardwareInfo);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 854ea43..630d819 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -266,6 +266,15 @@
     public static final int INPUT_STATE_DISCONNECTED = 2;
 
     /**
+     * An unknown state of the client pid gets from the TvInputManager. Client gets this value when
+     * query through {@link getClientPid(String sessionId)} fails.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int UNKNOWN_CLIENT_PID = -1;
+
+    /**
      * Broadcast intent action when the user blocked content ratings change. For use with the
      * {@link #isRatingBlocked}.
      */
@@ -1484,6 +1493,21 @@
     }
 
     /**
+     * Get a the client pid when creating the session with the session id provided.
+     *
+     * @param sessionId a String of session id that is used to query the client pid.
+     * @return the client pid when created the session. Returns {@link #UNKNOWN_CLIENT_PID}
+     *         if the call fails.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
+    public int getClientPid(@NonNull String sessionId) {
+        return getClientPidInternal(sessionId);
+    };
+
+    /**
      * Creates a recording {@link Session} for a given TV input.
      *
      * <p>The number of sessions that can be created at the same time is limited by the capability
@@ -1516,6 +1540,17 @@
         }
     }
 
+    private int getClientPidInternal(String sessionId) {
+        Preconditions.checkNotNull(sessionId);
+        int clientPid = UNKNOWN_CLIENT_PID;
+        try {
+            clientPid = mService.getClientPid(sessionId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return clientPid;
+    }
+
     /**
      * Returns the TvStreamConfig list of the given TV input.
      *
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 7fbb337..629dc7c 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -124,7 +124,7 @@
 
             @Override
             public void createSession(InputChannel channel, ITvInputSessionCallback cb,
-                    String inputId) {
+                    String inputId, String sessionId) {
                 if (channel == null) {
                     Log.w(TAG, "Creating session without input channel");
                 }
@@ -135,17 +135,20 @@
                 args.arg1 = channel;
                 args.arg2 = cb;
                 args.arg3 = inputId;
+                args.arg4 = sessionId;
                 mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget();
             }
 
             @Override
-            public void createRecordingSession(ITvInputSessionCallback cb, String inputId) {
+            public void createRecordingSession(ITvInputSessionCallback cb, String inputId,
+                    String sessionId) {
                 if (cb == null) {
                     return;
                 }
                 SomeArgs args = SomeArgs.obtain();
                 args.arg1 = cb;
                 args.arg2 = inputId;
+                args.arg3 = sessionId;
                 mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_RECORDING_SESSION, args)
                         .sendToTarget();
             }
@@ -208,6 +211,37 @@
     }
 
     /**
+     * Returns a concrete implementation of {@link Session}.
+     *
+     * <p>For any apps that needs sessionId to request tuner resources from TunerResourceManager,
+     * it needs to override this method to get the sessionId passed. When no overriding, this method
+     * calls {@link #onCreateSession(String)} defaultly.
+     *
+     * @param inputId The ID of the TV input associated with the session.
+     * @param sessionId the unique sessionId created by TIF when session is created.
+     */
+    @Nullable
+    public Session onCreateSession(@NonNull String inputId, @NonNull String sessionId) {
+        return onCreateSession(inputId);
+    }
+
+    /**
+     * Returns a concrete implementation of {@link RecordingSession}.
+     *
+     * <p>For any apps that needs sessionId to request tuner resources from TunerResourceManager,
+     * it needs to override this method to get the sessionId passed. When no overriding, this method
+     * calls {@link #onCreateRecordingSession(String)} defaultly.
+     *
+     * @param inputId The ID of the TV input associated with the recording session.
+     * @param sessionId the unique sessionId created by TIF when session is created.
+     */
+    @Nullable
+    public RecordingSession onCreateRecordingSession(
+            @NonNull String inputId, @NonNull String sessionId) {
+        return onCreateRecordingSession(inputId);
+    }
+
+    /**
      * Returns a new {@link TvInputInfo} object if this service is responsible for
      * {@code hardwareInfo}; otherwise, return {@code null}. Override to modify default behavior of
      * ignoring all hardware input.
@@ -2032,8 +2066,9 @@
                     InputChannel channel = (InputChannel) args.arg1;
                     ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
                     String inputId = (String) args.arg3;
+                    String sessionId = (String) args.arg4;
                     args.recycle();
-                    Session sessionImpl = onCreateSession(inputId);
+                    Session sessionImpl = onCreateSession(inputId, sessionId);
                     if (sessionImpl == null) {
                         try {
                             // Failed to create a session.
@@ -2103,8 +2138,10 @@
                     SomeArgs args = (SomeArgs) msg.obj;
                     ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg1;
                     String inputId = (String) args.arg2;
+                    String sessionId = (String) args.arg3;
                     args.recycle();
-                    RecordingSession recordingSessionImpl = onCreateRecordingSession(inputId);
+                    RecordingSession recordingSessionImpl =
+                            onCreateRecordingSession(inputId, sessionId);
                     if (recordingSessionImpl == null) {
                         try {
                             // Failed to create a recording session.
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index 4318a0a..5e0b1ea 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -318,7 +318,8 @@
      * @param flags The flags used for parceling.
      */
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Preconditions.checkNotNull(dest);
         dest.writeInt(mType);
         dest.writeString(mId);
         dest.writeString(mLanguage);
@@ -387,11 +388,13 @@
     public static final @android.annotation.NonNull Parcelable.Creator<TvTrackInfo> CREATOR =
             new Parcelable.Creator<TvTrackInfo>() {
                 @Override
+                @NonNull
                 public TvTrackInfo createFromParcel(Parcel in) {
                     return new TvTrackInfo(in);
                 }
 
                 @Override
+                @NonNull
                 public TvTrackInfo[] newArray(int size) {
                     return new TvTrackInfo[size];
                 }
@@ -444,7 +447,9 @@
          *
          * @param language The language string encoded by either ISO 639-1 or ISO 639-2/T.
          */
-        public final Builder setLanguage(String language) {
+        @NonNull
+        public  Builder setLanguage(@NonNull String language) {
+            Preconditions.checkNotNull(language);
             mLanguage = language;
             return this;
         }
@@ -454,7 +459,9 @@
          *
          * @param description The user readable description.
          */
-        public final Builder setDescription(CharSequence description) {
+        @NonNull
+        public  Builder setDescription(@NonNull CharSequence description) {
+            Preconditions.checkNotNull(description);
             mDescription = description;
             return this;
         }
@@ -479,7 +486,8 @@
          * @param audioChannelCount The audio channel count.
          * @throws IllegalStateException if not called on an audio track
          */
-        public final Builder setAudioChannelCount(int audioChannelCount) {
+        @NonNull
+        public Builder setAudioChannelCount(int audioChannelCount) {
             if (mType != TYPE_AUDIO) {
                 throw new IllegalStateException("Not an audio track");
             }
@@ -494,7 +502,8 @@
          * @param audioSampleRate The audio sample rate.
          * @throws IllegalStateException if not called on an audio track
          */
-        public final Builder setAudioSampleRate(int audioSampleRate) {
+        @NonNull
+        public Builder setAudioSampleRate(int audioSampleRate) {
             if (mType != TYPE_AUDIO) {
                 throw new IllegalStateException("Not an audio track");
             }
@@ -570,7 +579,8 @@
          * @param videoWidth The width of the video.
          * @throws IllegalStateException if not called on a video track
          */
-        public final Builder setVideoWidth(int videoWidth) {
+        @NonNull
+        public Builder setVideoWidth(int videoWidth) {
             if (mType != TYPE_VIDEO) {
                 throw new IllegalStateException("Not a video track");
             }
@@ -585,7 +595,8 @@
          * @param videoHeight The height of the video.
          * @throws IllegalStateException if not called on a video track
          */
-        public final Builder setVideoHeight(int videoHeight) {
+        @NonNull
+        public Builder setVideoHeight(int videoHeight) {
             if (mType != TYPE_VIDEO) {
                 throw new IllegalStateException("Not a video track");
             }
@@ -600,7 +611,8 @@
          * @param videoFrameRate The frame rate of the video.
          * @throws IllegalStateException if not called on a video track
          */
-        public final Builder setVideoFrameRate(float videoFrameRate) {
+        @NonNull
+        public Builder setVideoFrameRate(float videoFrameRate) {
             if (mType != TYPE_VIDEO) {
                 throw new IllegalStateException("Not a video track");
             }
@@ -620,7 +632,8 @@
          * @param videoPixelAspectRatio The pixel aspect ratio of the video.
          * @throws IllegalStateException if not called on a video track
          */
-        public final Builder setVideoPixelAspectRatio(float videoPixelAspectRatio) {
+        @NonNull
+        public Builder setVideoPixelAspectRatio(float videoPixelAspectRatio) {
             if (mType != TYPE_VIDEO) {
                 throw new IllegalStateException("Not a video track");
             }
@@ -640,7 +653,8 @@
          * @param videoActiveFormatDescription The AFD code of the video.
          * @throws IllegalStateException if not called on a video track
          */
-        public final Builder setVideoActiveFormatDescription(byte videoActiveFormatDescription) {
+        @NonNull
+        public Builder setVideoActiveFormatDescription(byte videoActiveFormatDescription) {
             if (mType != TYPE_VIDEO) {
                 throw new IllegalStateException("Not a video track");
             }
@@ -653,7 +667,9 @@
          *
          * @param extra The extra information.
          */
-        public final Builder setExtra(Bundle extra) {
+        @NonNull
+        public Builder setExtra(@NonNull Bundle extra) {
+            Preconditions.checkNotNull(extra);
             mExtra = new Bundle(extra);
             return this;
         }
@@ -663,6 +679,7 @@
          *
          * @return The new {@link TvTrackInfo} instance
          */
+        @NonNull
         public TvTrackInfo build() {
             return new TvTrackInfo(mType, mId, mLanguage, mDescription, mEncrypted,
                     mAudioChannelCount, mAudioSampleRate, mAudioDescription, mHardOfHearing,
diff --git a/media/java/android/media/tv/tuner/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java
index bda166e..83abf86 100644
--- a/media/java/android/media/tv/tuner/DemuxCapabilities.java
+++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java
@@ -16,11 +16,32 @@
 
 package android.media.tv.tuner;
 
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.Size;
+import android.media.tv.tuner.filter.FilterConfiguration;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Capabilities info for Demux.
+ *
  * @hide
  */
 public class DemuxCapabilities {
+
+    /** @hide */
+    @IntDef(flag = true, value = {
+            FilterConfiguration.FILTER_TYPE_TS,
+            FilterConfiguration.FILTER_TYPE_MMTP,
+            FilterConfiguration.FILTER_TYPE_IP,
+            FilterConfiguration.FILTER_TYPE_TLV,
+            FilterConfiguration.FILTER_TYPE_ALP
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FilterCapabilities {}
+
     private final int mNumDemux;
     private final int mNumRecord;
     private final int mNumPlayback;
@@ -34,7 +55,8 @@
     private final int mFilterCaps;
     private final int[] mLinkCaps;
 
-    DemuxCapabilities(int numDemux, int numRecord, int numPlayback, int numTsFilter,
+    // Used by JNI
+    private DemuxCapabilities(int numDemux, int numRecord, int numPlayback, int numTsFilter,
             int numSectionFilter, int numAudioFilter, int numVideoFilter, int numPesFilter,
             int numPcrFilter, int numBytesInSectionFilter, int filterCaps, int[] linkCaps) {
         mNumDemux = numDemux;
@@ -51,52 +73,73 @@
         mLinkCaps = linkCaps;
     }
 
-    /** Gets total number of demuxes. */
+    /**
+     * Gets total number of demuxes.
+     */
     public int getNumDemux() {
         return mNumDemux;
     }
-    /** Gets max number of recordings at a time. */
+    /**
+     * Gets max number of recordings at a time.
+     */
     public int getNumRecord() {
         return mNumRecord;
     }
-    /** Gets max number of playbacks at a time. */
+    /**
+     * Gets max number of playbacks at a time.
+     */
     public int getNumPlayback() {
         return mNumPlayback;
     }
-    /** Gets number of TS filters. */
+    /**
+     * Gets number of TS filters.
+     */
     public int getNumTsFilter() {
         return mNumTsFilter;
     }
-    /** Gets number of section filters. */
+    /**
+     * Gets number of section filters.
+     */
     public int getNumSectionFilter() {
         return mNumSectionFilter;
     }
-    /** Gets number of audio filters. */
+    /**
+     * Gets number of audio filters.
+     */
     public int getNumAudioFilter() {
         return mNumAudioFilter;
     }
-    /** Gets number of video filters. */
+    /**
+     * Gets number of video filters.
+     */
     public int getNumVideoFilter() {
         return mNumVideoFilter;
     }
-    /** Gets number of PES filters. */
+    /**
+     * Gets number of PES filters.
+     */
     public int getNumPesFilter() {
         return mNumPesFilter;
     }
-    /** Gets number of PCR filters. */
+    /**
+     * Gets number of PCR filters.
+     */
     public int getNumPcrFilter() {
         return mNumPcrFilter;
     }
-    /** Gets number of bytes in the mask of a section filter. */
+    /**
+     * Gets number of bytes in the mask of a section filter.
+     */
     public int getNumBytesInSectionFilter() {
         return mNumBytesInSectionFilter;
     }
     /**
      * Gets filter capabilities in bit field.
      *
-     * The bits of the returned value is corresponding to the types in
-     * {@link TunerConstants.FilterType}.
+     * <p>The bits of the returned value is corresponding to the types in
+     * {@link FilterConfiguration}.
      */
+    @FilterCapabilities
     public int getFilterCapabilities() {
         return mFilterCaps;
     }
@@ -104,10 +147,12 @@
     /**
      * Gets link capabilities.
      *
-     * The returned array contains the same elements as the number of types in
-     * {@link TunerConstants.FilterType}.
-     * The ith element represents the filter's capability as the source for the ith type
+     * <p>The returned array contains the same elements as the number of types in
+     * {@link FilterConfiguration}.
+     * <p>The ith element represents the filter's capability as the source for the ith type.
      */
+    @Nullable
+    @Size(5)
     public int[] getLinkCapabilities() {
         return mLinkCaps;
     }
diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java
index f9f7a22..0143582 100644
--- a/media/java/android/media/tv/tuner/Descrambler.java
+++ b/media/java/android/media/tv/tuner/Descrambler.java
@@ -16,9 +16,12 @@
 
 package android.media.tv.tuner;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.media.tv.tuner.Tuner.Filter;
-import android.media.tv.tuner.TunerConstants.DemuxPidType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * This class is used to interact with descramblers.
@@ -26,9 +29,25 @@
  * <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 {
+    /** @hide */
+    @IntDef(prefix = "PID_TYPE_", value = {PID_TYPE_T, PID_TYPE_MMPT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PidType {}
+
+    /**
+     * Packet ID is used to specify packets in transport stream.
+     */
+    public static final int PID_TYPE_T = 1;
+    /**
+     * Packet ID is used to specify packets in MMTP.
+     */
+    public static final int PID_TYPE_MMPT = 2;
+
+
     private long mNativeContext;
 
     private native int nativeAddPid(int pidType, int pid, Filter filter);
@@ -52,10 +71,8 @@
      * @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) {
+    public int addPid(@PidType int pidType, int pid, @Nullable Filter filter) {
         return nativeAddPid(pidType, pid, filter);
     }
 
@@ -68,10 +85,8 @@
      * @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) {
+    public int removePid(@PidType int pidType, int pid, @Nullable Filter filter) {
         return nativeRemovePid(pidType, pid, filter);
     }
 
@@ -83,17 +98,13 @@
      *
      * @param keyToken the token to be used to link the key slot.
      * @return result status of the operation.
-     *
-     * @hide
      */
-    public int setKeyToken(byte[] keyToken) {
+    public int setKeyToken(@Nullable byte[] keyToken) {
         return nativeSetKeyToken(keyToken);
     }
 
     /**
      * Release the descrambler instance.
-     *
-     * @hide
      */
     @Override
     public void close() {
diff --git a/media/java/android/media/tv/tuner/Dvr.java b/media/java/android/media/tv/tuner/Dvr.java
deleted file mode 100644
index 0bfba8f..0000000
--- a/media/java/android/media/tv/tuner/Dvr.java
+++ /dev/null
@@ -1,152 +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.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/DvrSettings.java b/media/java/android/media/tv/tuner/DvrSettings.java
deleted file mode 100644
index 76160dc..0000000
--- a/media/java/android/media/tv/tuner/DvrSettings.java
+++ /dev/null
@@ -1,124 +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.media.tv.tuner.TunerConstants.DataFormat;
-import android.media.tv.tuner.TunerConstants.DvrSettingsType;
-
-/**
- * DVR settings.
- *
- * @hide
- */
-public class DvrSettings {
-    private int mStatusMask;
-    private int mLowThreshold;
-    private int mHighThreshold;
-    private int mPacketSize;
-
-    @DataFormat
-    private int mDataFormat;
-    @DvrSettingsType
-    private int mType;
-
-    private DvrSettings(int statusMask, int lowThreshold, int highThreshold, int packetSize,
-            @DataFormat int dataFormat, @DvrSettingsType int type) {
-        mStatusMask = statusMask;
-        mLowThreshold = lowThreshold;
-        mHighThreshold = highThreshold;
-        mPacketSize = packetSize;
-        mDataFormat = dataFormat;
-        mType = type;
-    }
-
-    /**
-     * Creates a new builder.
-     */
-    public static Builder newBuilder() {
-        return new Builder();
-    }
-
-    /**
-     * Builder for DvrSettings.
-     */
-    public static final class Builder {
-        private int mStatusMask;
-        private int mLowThreshold;
-        private int mHighThreshold;
-        private int mPacketSize;
-        @DataFormat
-        private int mDataFormat;
-        @DvrSettingsType
-        private int mType;
-
-        /**
-         * Sets status mask.
-         */
-        public Builder setStatusMask(int statusMask) {
-            this.mStatusMask = statusMask;
-            return this;
-        }
-
-        /**
-         * Sets low threshold.
-         */
-        public Builder setLowThreshold(int lowThreshold) {
-            this.mLowThreshold = lowThreshold;
-            return this;
-        }
-
-        /**
-         * Sets high threshold.
-         */
-        public Builder setHighThreshold(int highThreshold) {
-            this.mHighThreshold = highThreshold;
-            return this;
-        }
-
-        /**
-         * Sets packet size.
-         */
-        public Builder setPacketSize(int packetSize) {
-            this.mPacketSize = packetSize;
-            return this;
-        }
-
-        /**
-         * Sets data format.
-         */
-        public Builder setDataFormat(@DataFormat int dataFormat) {
-            this.mDataFormat = dataFormat;
-            return this;
-        }
-
-        /**
-         * Sets settings type.
-         */
-        public Builder setType(@DvrSettingsType int type) {
-            this.mType = type;
-            return this;
-        }
-
-        /**
-         * Builds a DvrSettings instance.
-         */
-        public DvrSettings build() {
-            return new DvrSettings(
-                    mStatusMask, mLowThreshold, mHighThreshold, mPacketSize, mDataFormat, mType);
-        }
-    }
-}
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index f181b49..c7cc9e6d 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.hardware.tv.tuner.V1_0.Constants;
 import android.media.tv.tuner.Tuner.LnbCallback;
@@ -37,6 +38,7 @@
  *
  * @hide
  */
+@SystemApi
 public class Lnb implements AutoCloseable {
     /** @hide */
     @IntDef({VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V,
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index f8b46b3..862489f 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -25,8 +25,10 @@
 import android.media.tv.tuner.TunerConstants.FilterSubtype;
 import android.media.tv.tuner.TunerConstants.FrontendScanType;
 import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.dvr.Dvr;
 import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
 import android.media.tv.tuner.filter.FilterEvent;
+import android.media.tv.tuner.filter.TimeFilter;
 import android.media.tv.tuner.frontend.FrontendCallback;
 import android.media.tv.tuner.frontend.FrontendInfo;
 import android.media.tv.tuner.frontend.FrontendStatus;
@@ -415,8 +417,14 @@
         return mFrontend.mId;
     }
 
-    /** @hide */
-    private static DemuxCapabilities getDemuxCapabilities() {
+    /**
+     * Gets Demux capabilities.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @Nullable
+    public static DemuxCapabilities getDemuxCapabilities(@NonNull Context context) {
+        TunerUtils.checkTunerPermission(context);
         return nativeGetDemuxCapabilities();
     }
 
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index a55c485..c2d6c58c 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -51,30 +51,6 @@
 
 
     /** @hide */
-    @IntDef({DATA_FORMAT_TS, DATA_FORMAT_PES, DATA_FORMAT_ES, DATA_FORMAT_SHV_TLV})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DataFormat {}
-    /** @hide */
-    public static final int DATA_FORMAT_TS = Constants.DataFormat.TS;
-    /** @hide */
-    public static final int DATA_FORMAT_PES = Constants.DataFormat.PES;
-    /** @hide */
-    public static final int DATA_FORMAT_ES = Constants.DataFormat.ES;
-    /** @hide */
-    public static final int DATA_FORMAT_SHV_TLV = Constants.DataFormat.SHV_TLV;
-
-
-    /** @hide */
-    @IntDef({DEMUX_T_PID, DEMUX_MMPT_PID})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DemuxPidType {}
-    /** @hide */
-    public static final int DEMUX_T_PID = 1;
-    /** @hide */
-    public static final int DEMUX_MMPT_PID = 2;
-
-
-    /** @hide */
     @IntDef({FILTER_SUBTYPE_UNDEFINED, FILTER_SUBTYPE_SECTION, FILTER_SUBTYPE_PES,
             FILTER_SUBTYPE_AUDIO, FILTER_SUBTYPE_VIDEO, FILTER_SUBTYPE_DOWNLOAD,
             FILTER_SUBTYPE_RECORD, FILTER_SUBTYPE_TS, FILTER_SUBTYPE_PCR, FILTER_SUBTYPE_TEMI,
@@ -118,8 +94,8 @@
     public static final int FILTER_SUBTYPE_PTP = 16;
 
     /** @hide */
-    @IntDef({FILTER_STATUS_DATA_READY, FILTER_STATUS_LOW_WATER, FILTER_STATUS_HIGH_WATER,
-            FILTER_STATUS_OVERFLOW})
+    @IntDef(flag = true, prefix = "FILTER_STATUS_", value = {FILTER_STATUS_DATA_READY,
+            FILTER_STATUS_LOW_WATER, FILTER_STATUS_HIGH_WATER, FILTER_STATUS_OVERFLOW})
     @Retention(RetentionPolicy.SOURCE)
     public @interface FilterStatus {}
 
@@ -148,6 +124,187 @@
      */
     public static final int FILTER_STATUS_OVERFLOW = Constants.DemuxFilterStatus.OVERFLOW;
 
+    /**
+     * Indexes can be tagged through TS (Transport Stream) header.
+     *
+     * @hide
+     */
+    @IntDef(flag = true, value = {TS_INDEX_FIRST_PACKET, TS_INDEX_PAYLOAD_UNIT_START_INDICATOR,
+            TS_INDEX_CHANGE_TO_NOT_SCRAMBLED, TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED,
+            TS_INDEX_CHANGE_TO_ODD_SCRAMBLED, TS_INDEX_DISCONTINUITY_INDICATOR,
+            TS_INDEX_RANDOM_ACCESS_INDICATOR, TS_INDEX_PRIORITY_INDICATOR, TS_INDEX_PCR_FLAG,
+            TS_INDEX_OPCR_FLAG, TS_INDEX_SPLICING_POINT_FLAG, TS_INDEX_PRIVATE_DATA,
+            TS_INDEX_ADAPTATION_EXTENSION_FLAG})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TsIndex {}
+
+    /**
+     * TS index FIRST_PACKET.
+     * @hide
+     */
+    public static final int TS_INDEX_FIRST_PACKET = Constants.DemuxTsIndex.FIRST_PACKET;
+    /**
+     * TS index PAYLOAD_UNIT_START_INDICATOR.
+     * @hide
+     */
+    public static final int TS_INDEX_PAYLOAD_UNIT_START_INDICATOR =
+            Constants.DemuxTsIndex.PAYLOAD_UNIT_START_INDICATOR;
+    /**
+     * TS index CHANGE_TO_NOT_SCRAMBLED.
+     * @hide
+     */
+    public static final int TS_INDEX_CHANGE_TO_NOT_SCRAMBLED =
+            Constants.DemuxTsIndex.CHANGE_TO_NOT_SCRAMBLED;
+    /**
+     * TS index CHANGE_TO_EVEN_SCRAMBLED.
+     * @hide
+     */
+    public static final int TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED =
+            Constants.DemuxTsIndex.CHANGE_TO_EVEN_SCRAMBLED;
+    /**
+     * TS index CHANGE_TO_ODD_SCRAMBLED.
+     * @hide
+     */
+    public static final int TS_INDEX_CHANGE_TO_ODD_SCRAMBLED =
+            Constants.DemuxTsIndex.CHANGE_TO_ODD_SCRAMBLED;
+    /**
+     * TS index DISCONTINUITY_INDICATOR.
+     * @hide
+     */
+    public static final int TS_INDEX_DISCONTINUITY_INDICATOR =
+            Constants.DemuxTsIndex.DISCONTINUITY_INDICATOR;
+    /**
+     * TS index RANDOM_ACCESS_INDICATOR.
+     * @hide
+     */
+    public static final int TS_INDEX_RANDOM_ACCESS_INDICATOR =
+            Constants.DemuxTsIndex.RANDOM_ACCESS_INDICATOR;
+    /**
+     * TS index PRIORITY_INDICATOR.
+     * @hide
+     */
+    public static final int TS_INDEX_PRIORITY_INDICATOR = Constants.DemuxTsIndex.PRIORITY_INDICATOR;
+    /**
+     * TS index PCR_FLAG.
+     * @hide
+     */
+    public static final int TS_INDEX_PCR_FLAG = Constants.DemuxTsIndex.PCR_FLAG;
+    /**
+     * TS index OPCR_FLAG.
+     * @hide
+     */
+    public static final int TS_INDEX_OPCR_FLAG = Constants.DemuxTsIndex.OPCR_FLAG;
+    /**
+     * TS index SPLICING_POINT_FLAG.
+     * @hide
+     */
+    public static final int TS_INDEX_SPLICING_POINT_FLAG =
+            Constants.DemuxTsIndex.SPLICING_POINT_FLAG;
+    /**
+     * TS index PRIVATE_DATA.
+     * @hide
+     */
+    public static final int TS_INDEX_PRIVATE_DATA = Constants.DemuxTsIndex.PRIVATE_DATA;
+    /**
+     * TS index ADAPTATION_EXTENSION_FLAG.
+     * @hide
+     */
+    public static final int TS_INDEX_ADAPTATION_EXTENSION_FLAG =
+            Constants.DemuxTsIndex.ADAPTATION_EXTENSION_FLAG;
+
+    /**
+     * Indexes can be tagged by Start Code in PES (Packetized Elementary Stream)
+     * according to ISO/IEC 13818-1.
+     * @hide
+     */
+    @IntDef(flag = true, value = {SC_INDEX_I_FRAME, SC_INDEX_P_FRAME, SC_INDEX_B_FRAME,
+            SC_INDEX_SEQUENCE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ScIndex {}
+
+    /**
+     * SC index for a new I-frame.
+     * @hide
+     */
+    public static final int SC_INDEX_I_FRAME = Constants.DemuxScIndex.I_FRAME;
+    /**
+     * SC index for a new P-frame.
+     * @hide
+     */
+    public static final int SC_INDEX_P_FRAME = Constants.DemuxScIndex.P_FRAME;
+    /**
+     * SC index for a new B-frame.
+     * @hide
+     */
+    public static final int SC_INDEX_B_FRAME = Constants.DemuxScIndex.B_FRAME;
+    /**
+     * SC index for a new sequence.
+     * @hide
+     */
+    public static final int SC_INDEX_SEQUENCE = Constants.DemuxScIndex.SEQUENCE;
+
+
+    /**
+     * Indexes can be tagged by NAL unit group in HEVC according to ISO/IEC 23008-2.
+     *
+     * @hide
+     */
+    @IntDef(flag = true,
+            value = {SC_HEVC_INDEX_SPS, SC_HEVC_INDEX_AUD, SC_HEVC_INDEX_SLICE_CE_BLA_W_LP,
+            SC_HEVC_INDEX_SLICE_BLA_W_RADL, SC_HEVC_INDEX_SLICE_BLA_N_LP,
+            SC_HEVC_INDEX_SLICE_IDR_W_RADL, SC_HEVC_INDEX_SLICE_IDR_N_LP,
+            SC_HEVC_INDEX_SLICE_TRAIL_CRA})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ScHevcIndex {}
+
+    /**
+     * SC HEVC index SPS.
+     * @hide
+     */
+    public static final int SC_HEVC_INDEX_SPS = Constants.DemuxScHevcIndex.SPS;
+    /**
+     * SC HEVC index AUD.
+     * @hide
+     */
+    public static final int SC_HEVC_INDEX_AUD = Constants.DemuxScHevcIndex.AUD;
+    /**
+     * SC HEVC index SLICE_CE_BLA_W_LP.
+     * @hide
+     */
+    public static final int SC_HEVC_INDEX_SLICE_CE_BLA_W_LP =
+            Constants.DemuxScHevcIndex.SLICE_CE_BLA_W_LP;
+    /**
+     * SC HEVC index SLICE_BLA_W_RADL.
+     * @hide
+     */
+    public static final int SC_HEVC_INDEX_SLICE_BLA_W_RADL =
+            Constants.DemuxScHevcIndex.SLICE_BLA_W_RADL;
+    /**
+     * SC HEVC index SLICE_BLA_N_LP.
+     * @hide
+     */
+    public static final int SC_HEVC_INDEX_SLICE_BLA_N_LP =
+            Constants.DemuxScHevcIndex.SLICE_BLA_N_LP;
+    /**
+     * SC HEVC index SLICE_IDR_W_RADL.
+     * @hide
+     */
+    public static final int SC_HEVC_INDEX_SLICE_IDR_W_RADL =
+            Constants.DemuxScHevcIndex.SLICE_IDR_W_RADL;
+    /**
+     * SC HEVC index SLICE_IDR_N_LP.
+     * @hide
+     */
+    public static final int SC_HEVC_INDEX_SLICE_IDR_N_LP =
+            Constants.DemuxScHevcIndex.SLICE_IDR_N_LP;
+    /**
+     * SC HEVC index SLICE_TRAIL_CRA.
+     * @hide
+     */
+    public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA =
+            Constants.DemuxScHevcIndex.SLICE_TRAIL_CRA;
+
+
     /** @hide */
     @IntDef({FRONTEND_SCAN_UNDEFINED, FRONTEND_SCAN_AUTO, FRONTEND_SCAN_BLIND})
     @Retention(RetentionPolicy.SOURCE)
@@ -1132,15 +1289,6 @@
     /** @hide */
     public static final int FILTER_SETTINGS_ALP = Constants.DemuxFilterMainType.ALP;
 
-    /** @hide */
-    @IntDef({DVR_SETTINGS_RECORD, DVR_SETTINGS_PLAYBACK})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DvrSettingsType {}
-    /** @hide */
-    public static final int DVR_SETTINGS_RECORD = Constants.DvrType.RECORD;
-    /** @hide */
-    public static final int DVR_SETTINGS_PLAYBACK = Constants.DvrType.PLAYBACK;
-
 
     /** @hide */
     @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE,
@@ -1150,37 +1298,30 @@
 
     /**
      * Operation succeeded.
-     * @hide
      */
     public static final int RESULT_SUCCESS = Constants.Result.SUCCESS;
     /**
      * Operation failed because the corresponding resources are not available.
-     * @hide
      */
     public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE;
     /**
      * Operation failed because the corresponding resources are not initialized.
-     * @hide
      */
     public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED;
     /**
      * Operation failed because it's not in a valid state.
-     * @hide
      */
     public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE;
     /**
      * Operation failed because there are invalid arguments.
-     * @hide
      */
     public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT;
     /**
      * Memory allocation failed.
-     * @hide
      */
     public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY;
     /**
      * Operation failed due to unknown errors.
-     * @hide
      */
     public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR;
 
diff --git a/media/java/android/media/tv/tuner/dvr/Dvr.java b/media/java/android/media/tv/tuner/dvr/Dvr.java
new file mode 100644
index 0000000..f90042b
--- /dev/null
+++ b/media/java/android/media/tv/tuner/dvr/Dvr.java
@@ -0,0 +1,186 @@
+/*
+ * 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.dvr;
+
+import android.annotation.BytesLong;
+import android.annotation.NonNull;
+import android.media.tv.tuner.Tuner.DvrCallback;
+import android.media.tv.tuner.Tuner.Filter;
+import android.media.tv.tuner.TunerConstants.Result;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Digital Video Record (DVR) interface provides record control on Demux's output buffer and
+ * playback control on Demux's input buffer.
+ *
+ * @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(long size);
+    private native int nativeRead(byte[] bytes, long offset, long size);
+    private native int nativeWrite(long size);
+    private native int nativeWrite(byte[] bytes, long offset, long size);
+
+    private Dvr() {}
+
+    /**
+     * Attaches a filter to DVR interface for recording.
+     *
+     * @param filter the filter to be attached.
+     * @return result status of the operation.
+     */
+    @Result
+    public int attachFilter(@NonNull Filter filter) {
+        return nativeAttachFilter(filter);
+    }
+
+    /**
+     * Detaches a filter from DVR interface.
+     *
+     * @param filter the filter to be detached.
+     * @return result status of the operation.
+     */
+    @Result
+    public int detachFilter(@NonNull Filter filter) {
+        return nativeDetachFilter(filter);
+    }
+
+    /**
+     * Configures the DVR.
+     *
+     * @param settings the settings of the DVR interface.
+     * @return result status of the operation.
+     */
+    @Result
+    public int configure(@NonNull DvrSettings settings) {
+        return nativeConfigureDvr(settings);
+    }
+
+    /**
+     * Starts DVR.
+     *
+     * <p>Starts consuming playback data or producing data for recording.
+     *
+     * @return result status of the operation.
+     */
+    @Result
+    public int start() {
+        return nativeStartDvr();
+    }
+
+    /**
+     * Stops DVR.
+     *
+     * <p>Stops consuming playback data or producing data for recording.
+     *
+     * @return result status of the operation.
+     */
+    @Result
+    public int stop() {
+        return nativeStopDvr();
+    }
+
+    /**
+     * Flushed DVR data.
+     *
+     * <p>The data in DVR buffer is cleared.
+     *
+     * @return result status of the operation.
+     */
+    @Result
+    public int flush() {
+        return nativeFlushDvr();
+    }
+
+    /**
+     * Closes the DVR instance to release resources.
+     *
+     * @return result status of the operation.
+     */
+    @Result
+    public int close() {
+        return nativeClose();
+    }
+
+    /**
+     * Sets file descriptor to read/write data.
+     *
+     * @param fd the file descriptor to read/write data.
+     */
+    public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
+        nativeSetFileDescriptor(fd.getFd());
+    }
+
+    /**
+     * Reads data from the file for DVR playback.
+     *
+     * @param size the maximum number of bytes to read.
+     * @return the number of bytes read.
+     */
+    public int read(@BytesLong long size) {
+        return nativeRead(size);
+    }
+
+    /**
+     * Reads data from the buffer for DVR playback and copies to the given byte array.
+     *
+     * @param bytes the byte array to store the data.
+     * @param offset the index of the first byte in {@code bytes} to copy to.
+     * @param size the maximum number of bytes to read.
+     * @return the number of bytes read.
+     */
+    public int read(@NonNull byte[] bytes, @BytesLong long offset, @BytesLong long 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.
+     *
+     * @param size the maximum number of bytes to write.
+     * @return the number of bytes written.
+     */
+    public int write(@BytesLong long size) {
+        return nativeWrite(size);
+    }
+
+    /**
+     * Writes recording data to buffer.
+     *
+     * @param bytes the byte array stores the data to be written to DVR.
+     * @param offset the index of the first byte in {@code bytes} to be written to DVR.
+     * @param size the maximum number of bytes to write.
+     * @return the number of bytes written.
+     */
+    public int write(@NonNull byte[] bytes, @BytesLong long offset, @BytesLong long size) {
+        return nativeWrite(bytes, offset, size);
+    }
+}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrSettings.java b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
new file mode 100644
index 0000000..46efd7a
--- /dev/null
+++ b/media/java/android/media/tv/tuner/dvr/DvrSettings.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 android.media.tv.tuner.dvr;
+
+import android.annotation.BytesLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.TunerConstants.FilterStatus;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * DVR settings used to configure {@link Dvr}.
+ *
+ * @hide
+ */
+public class DvrSettings {
+
+    /** @hide */
+    @IntDef(prefix = "DATA_FORMAT_",
+            value = {DATA_FORMAT_TS, DATA_FORMAT_PES, DATA_FORMAT_ES, DATA_FORMAT_SHV_TLV})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DataFormat {}
+
+    /**
+     * Transport Stream.
+     */
+    public static final int DATA_FORMAT_TS = Constants.DataFormat.TS;
+    /**
+     * Packetized Elementary Stream.
+     */
+    public static final int DATA_FORMAT_PES = Constants.DataFormat.PES;
+    /**
+     * Elementary Stream.
+     */
+    public static final int DATA_FORMAT_ES = Constants.DataFormat.ES;
+    /**
+     * TLV (type-length-value) Stream for SHV
+     */
+    public static final int DATA_FORMAT_SHV_TLV = Constants.DataFormat.SHV_TLV;
+
+
+    /** @hide */
+    @IntDef(prefix = "TYPE_", value = {TYPE_RECORD, TYPE_PLAYBACK})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    /**
+     * DVR for recording.
+     */
+    public static final int TYPE_RECORD = Constants.DvrType.RECORD;
+    /**
+     * DVR for playback of recorded programs.
+     */
+    public static final int TYPE_PLAYBACK = Constants.DvrType.PLAYBACK;
+
+
+
+    private final int mStatusMask;
+    private final long mLowThreshold;
+    private final long mHighThreshold;
+    private final long mPacketSize;
+
+    @DataFormat
+    private final int mDataFormat;
+    @Type
+    private final int mType;
+
+    private DvrSettings(int statusMask, long lowThreshold, long highThreshold, long packetSize,
+            @DataFormat int dataFormat, @Type int type) {
+        mStatusMask = statusMask;
+        mLowThreshold = lowThreshold;
+        mHighThreshold = highThreshold;
+        mPacketSize = packetSize;
+        mDataFormat = dataFormat;
+        mType = type;
+    }
+
+    /**
+     * Creates a builder for {@link DvrSettings}.
+     */
+    @NonNull
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link DvrSettings}.
+     */
+    public static final class Builder {
+        private int mStatusMask;
+        private long mLowThreshold;
+        private long mHighThreshold;
+        private long mPacketSize;
+        @DataFormat
+        private int mDataFormat;
+        @Type
+        private int mType;
+
+        /**
+         * Sets status mask.
+         */
+        @NonNull
+        public Builder setStatusMask(@FilterStatus int statusMask) {
+            this.mStatusMask = statusMask;
+            return this;
+        }
+
+        /**
+         * Sets low threshold in bytes.
+         */
+        @NonNull
+        public Builder setLowThreshold(@BytesLong long lowThreshold) {
+            this.mLowThreshold = lowThreshold;
+            return this;
+        }
+
+        /**
+         * Sets high threshold in bytes.
+         */
+        @NonNull
+        public Builder setHighThreshold(@BytesLong long highThreshold) {
+            this.mHighThreshold = highThreshold;
+            return this;
+        }
+
+        /**
+         * Sets packet size in bytes.
+         */
+        @NonNull
+        public Builder setPacketSize(@BytesLong long packetSize) {
+            this.mPacketSize = packetSize;
+            return this;
+        }
+
+        /**
+         * Sets data format.
+         */
+        @NonNull
+        public Builder setDataFormat(@DataFormat int dataFormat) {
+            this.mDataFormat = dataFormat;
+            return this;
+        }
+
+        /**
+         * Sets settings type.
+         */
+        @NonNull
+        public Builder setType(@Type int type) {
+            this.mType = type;
+            return this;
+        }
+
+        /**
+         * Builds a {@link DvrSettings} object.
+         */
+        @NonNull
+        public DvrSettings build() {
+            return new DvrSettings(
+                    mStatusMask, mLowThreshold, mHighThreshold, mPacketSize, mDataFormat, mType);
+        }
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/AudioDescriptor.java b/media/java/android/media/tv/tuner/filter/AudioDescriptor.java
new file mode 100644
index 0000000..c88c07f
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/AudioDescriptor.java
@@ -0,0 +1,106 @@
+/*
+ * 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;
+
+/**
+ * Meta data from AD (Audio Descriptor) according to ETSI TS 101 154 V2.1.1.
+ *
+ * @hide
+ */
+public class AudioDescriptor {
+    private final byte mAdFade;
+    private final byte mAdPan;
+    private final char mVersionTextTag;
+    private final byte mAdGainCenter;
+    private final byte mAdGainFront;
+    private final byte mAdGainSurround;
+
+    // This constructor is used by JNI code only
+    private AudioDescriptor(byte adFade, byte adPan, char versionTextTag, byte adGainCenter,
+            byte adGainFront, byte adGainSurround) {
+        mAdFade = adFade;
+        mAdPan = adPan;
+        mVersionTextTag = versionTextTag;
+        mAdGainCenter = adGainCenter;
+        mAdGainFront = adGainFront;
+        mAdGainSurround = adGainSurround;
+    }
+
+    /**
+     * Gets AD fade byte.
+     *
+     * <p>Takes values between 0x00 (representing no fade of the main programme sound) and 0xFF
+     * (representing a full fade). Over the range 0x00 to 0xFE one lsb represents a step in
+     * attenuation of the programme sound of 0.3 dB giving a range of 76.2 dB. The fade value of
+     * 0xFF represents no programme sound at all (i.e. mute).
+     */
+    public byte getAdFade() {
+        return mAdFade;
+    }
+
+    /**
+     * Gets AD pan byte.
+     *
+     * <p>Takes values between 0x00 representing a central forward presentation of the audio
+     * description and 0xFF, each increment representing a 360/256 degree step clockwise looking
+     * down on the listener (i.e. just over 1.4 degrees).
+     */
+    public byte getAdPan() {
+        return mAdPan;
+    }
+
+    /**
+     * Gets AD version tag. A single ASCII character version indicates the version.
+     *
+     * <p>A single ASCII character version designator (here "1" indicates revision 1).
+     */
+    public char getVersionTextTag() {
+        return mVersionTextTag;
+    }
+
+    /**
+     * Gets AD gain byte center in dB.
+     *
+     * <p>Represents a signed value in dB. Takes values between 0x7F (representing +76.2 dB boost of
+     * the main programme center) and 0x80 (representing a full fade). Over the range 0x00 to 0x7F
+     * one lsb represents a step in boost of the programme center of 0.6 dB giving a maximum boost
+     * of +76.2 dB. Over the range 0x81 to 0x00 one lsb represents a step in attenuation of the
+     * programme center of 0.6 dB giving a maximum attenuation of -76.2 dB. The gain value of 0x80
+     * represents no main center level at all (i.e. mute).
+     */
+    public byte getAdGainCenter() {
+        return mAdGainCenter;
+    }
+
+    /**
+     * Gets AD gain byte front in dB.
+     *
+     * <p>Same as {@link #getAdGainCenter()}, but applied to left and right front channel.
+     */
+    public byte getAdGainFront() {
+        return mAdGainFront;
+    }
+
+    /**
+     * Gets AD gain byte surround in dB.
+     *
+     * <p>Same as {@link #getAdGainCenter()}, but applied to all surround channels
+     */
+    public byte getAdGainSurround() {
+        return mAdGainSurround;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/filter/AudioExtraMetaData.java b/media/java/android/media/tv/tuner/filter/AudioExtraMetaData.java
deleted file mode 100644
index 306de84..0000000
--- a/media/java/android/media/tv/tuner/filter/AudioExtraMetaData.java
+++ /dev/null
@@ -1,31 +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.filter;
-
-/**
- * Extra Meta Data from AD (Audio Descriptor) according to
- * ETSI TS 101 154 V2.1.1.
- * @hide
- */
-public class AudioExtraMetaData {
-    private byte mAdFade;
-    private byte mAdPan;
-    private byte mVersionTextTag;
-    private byte mAdGainCenter;
-    private byte mAdGainFront;
-    private byte mAdGainSurround;
-}
diff --git a/media/java/android/media/tv/tuner/filter/DownloadEvent.java b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
index 548fa77..591e4e5 100644
--- a/media/java/android/media/tv/tuner/filter/DownloadEvent.java
+++ b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
@@ -16,15 +16,65 @@
 
 package android.media.tv.tuner.filter;
 
+import android.media.tv.tuner.Tuner.Filter;
+
 /**
- * Download event.
+ * Filter event sent from {@link Filter} objects with download type.
+ *
  * @hide
  */
 public class DownloadEvent extends FilterEvent {
-    private int mItemId;
-    private int mMpuSequenceNumber;
-    private int mItemFragmentIndex;
-    private int mLastItemFragmentIndex;
-    private int mDataLength;
+    private final int mItemId;
+    private final int mMpuSequenceNumber;
+    private final int mItemFragmentIndex;
+    private final int mLastItemFragmentIndex;
+    private final int mDataLength;
+
+    // This constructor is used by JNI code only
+    private DownloadEvent(int itemId, int mpuSequenceNumber, int itemFragmentIndex,
+            int lastItemFragmentIndex, int dataLength) {
+        mItemId = itemId;
+        mMpuSequenceNumber = mpuSequenceNumber;
+        mItemFragmentIndex = itemFragmentIndex;
+        mLastItemFragmentIndex = lastItemFragmentIndex;
+        mDataLength = dataLength;
+    }
+
+    /**
+     * Gets item ID.
+     */
+    public int getItemId() {
+        return mItemId;
+    }
+
+    /**
+     * Gets MPU sequence number of filtered data.
+     */
+    public int getMpuSequenceNumber() {
+        return mMpuSequenceNumber;
+    }
+
+    /**
+     * Gets current index of the current item.
+     *
+     * An item can be stored in different fragments.
+     */
+    public int getItemFragmentIndex() {
+        return mItemFragmentIndex;
+    }
+
+    /**
+     * Gets last index of the current item.
+     */
+    public int getLastItemFragmentIndex() {
+        return mLastItemFragmentIndex;
+    }
+
+    /**
+     * Gets data size in bytes of filtered data.
+     */
+    public int getDataLength() {
+        return mDataLength;
+    }
 }
 
diff --git a/media/java/android/media/tv/tuner/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
similarity index 70%
rename from media/java/android/media/tv/tuner/Filter.java
rename to media/java/android/media/tv/tuner/filter/Filter.java
index db3b97a..804c0c5 100644
--- a/media/java/android/media/tv/tuner/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -14,33 +14,33 @@
  * limitations under the License.
  */
 
-package android.media.tv.tuner;
+package android.media.tv.tuner.filter;
 
+import android.annotation.BytesLong;
 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.
+ * <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 final 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 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 nativeRead(byte[] buffer, long offset, long size);
     private native int nativeClose();
 
     private Filter(int id) {
@@ -53,24 +53,20 @@
     /**
      * Configures the filter.
      *
-     * @param settings the settings of the filter.
+     * @param config the configuration of the filter.
      * @return result status of the operation.
-     * @hide
      */
-    public int configure(FilterConfiguration settings) {
+    public int configure(@NonNull FilterConfiguration config) {
         int subType = -1;
-        Settings s = settings.getSettings();
+        Settings s = config.getSettings();
         if (s != null) {
             subType = s.getType();
         }
-        return nativeConfigureFilter(settings.getType(), subType, settings);
+        return nativeConfigureFilter(config.getType(), subType, config);
     }
 
     /**
      * Gets the filter Id.
-     *
-     * @return the hardware resource Id for the filter.
-     * @hide
      */
     public int getId() {
         return nativeGetId();
@@ -87,17 +83,15 @@
      * @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) {
+    public int setDataSource(@Nullable Filter source) {
         return nativeSetDataSource(source);
     }
 
     /**
-     * Starts the filter.
+     * Starts filtering data.
      *
      * @return result status of the operation.
-     * @hide
      */
     public int start() {
         return nativeStartFilter();
@@ -105,35 +99,38 @@
 
 
     /**
-     * Stops the filter.
+     * Stops filtering data.
      *
      * @return result status of the operation.
-     * @hide
      */
     public int stop() {
         return nativeStopFilter();
     }
 
     /**
-     * Flushes the filter.
+     * Flushes the filter. Data in filter buffer is cleared.
      *
      * @return result status of the operation.
-     * @hide
      */
     public int flush() {
         return nativeFlushFilter();
     }
 
-    /** @hide */
-    public int read(@NonNull byte[] buffer, int offset, int size) {
+    /**
+     * Copies filtered data from filter buffer to the given byte array.
+     *
+     * @param buffer the buffer to store the filtered data.
+     * @param offset the index of the first byte in {@code buffer} to write.
+     * @param size the maximum number of bytes to read.
+     * @return the number of bytes read.
+     */
+    public int read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
         size = Math.min(size, buffer.length - offset);
         return nativeRead(buffer, offset, size);
     }
 
     /**
-     * Release the Filter instance.
-     *
-     * @hide
+     * Releases the Filter instance.
      */
     @Override
     public void close() {
diff --git a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
index 99b10cd..6496627 100644
--- a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
 
 import java.lang.annotation.Retention;
@@ -28,6 +29,7 @@
  *
  * @hide
  */
+@SystemApi
 public abstract class FilterConfiguration {
 
     /** @hide */
@@ -57,7 +59,7 @@
     public static final int FILTER_TYPE_ALP = Constants.DemuxFilterMainType.ALP;
 
     @Nullable
-    private final Settings mSettings;
+    /* package */ final Settings mSettings;
 
     /* package */ FilterConfiguration(Settings settings) {
         mSettings = settings;
diff --git a/media/java/android/media/tv/tuner/filter/IpPayloadEvent.java b/media/java/android/media/tv/tuner/filter/IpPayloadEvent.java
index 4da1d21..09489ed 100644
--- a/media/java/android/media/tv/tuner/filter/IpPayloadEvent.java
+++ b/media/java/android/media/tv/tuner/filter/IpPayloadEvent.java
@@ -16,10 +16,25 @@
 
 package android.media.tv.tuner.filter;
 
+import android.media.tv.tuner.Tuner.Filter;
+
 /**
- * IP payload event.
+ * Filter event sent from {@link Filter} objects with IP payload type.
+ *
  * @hide
  */
 public class IpPayloadEvent extends FilterEvent {
-    private int mDataLength;
+    private final int mDataLength;
+
+    // This constructor is used by JNI code only
+    private IpPayloadEvent(int dataLength) {
+        mDataLength = dataLength;
+    }
+
+    /**
+     * Gets data size in bytes of filtered data.
+     */
+    public int getDataLength() {
+        return mDataLength;
+    }
 }
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index 7703248..37f94ae 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -16,20 +16,111 @@
 
 package android.media.tv.tuner.filter;
 
-import android.os.NativeHandle;
+import android.annotation.Nullable;
+import android.media.tv.tuner.Tuner.Filter;
 
 /**
- * Media event.
+ * Filter event sent from {@link Filter} objects with media type.
+ *
  * @hide
  */
-public class MediaEvent extends FilterEvent {
-    private int mStreamId;
-    private boolean mIsPtsPresent;
-    private long mPts;
-    private int mDataLength;
-    private NativeHandle mHandle;
-    private boolean mIsSecureMemory;
-    private int mMpuSequenceNumber;
-    private boolean mIsPrivateData;
-    private AudioExtraMetaData mExtraMetaData;
+public class MediaEvent extends FilterEvent{
+    private final int mStreamId;
+    private final boolean mIsPtsPresent;
+    private final long mPts;
+    private final int mDataLength;
+    private final Object mLinearBuffer;
+    private final boolean mIsSecureMemory;
+    private final int mMpuSequenceNumber;
+    private final boolean mIsPrivateData;
+    private final AudioDescriptor mExtraMetaData;
+
+    // This constructor is used by JNI code only
+    private MediaEvent(int streamId, boolean isPtsPresent, long pts, int dataLength, Object buffer,
+            boolean isSecureMemory, int mpuSequenceNumber, boolean isPrivateData,
+            AudioDescriptor extraMetaData) {
+        mStreamId = streamId;
+        mIsPtsPresent = isPtsPresent;
+        mPts = pts;
+        mDataLength = dataLength;
+        mLinearBuffer = buffer;
+        mIsSecureMemory = isSecureMemory;
+        mMpuSequenceNumber = mpuSequenceNumber;
+        mIsPrivateData = isPrivateData;
+        mExtraMetaData = extraMetaData;
+    }
+
+    /**
+     * Gets stream ID.
+     */
+    public int getStreamId() {
+        return mStreamId;
+    }
+
+    /**
+     * Returns whether PTS is present.
+     *
+     * @return {@code true} if PTS is present in PES header; {@code false} otherwise.
+     */
+    public boolean getIsPtsPresent() {
+        return mIsPtsPresent;
+    }
+
+    /**
+     * Gets PTS (Presentation Time Stamp) for audio or video frame.
+     */
+    public long getPts() {
+        return mPts;
+    }
+
+    /**
+     * Gets data size in bytes of audio or video frame.
+     */
+    public int getDataLength() {
+        return mDataLength;
+    }
+
+    /**
+     * Gets a linear buffer associated to the memory where audio or video data stays.
+     * TODO: use LinearBuffer when it's ready.
+     *
+     * @hide
+     */
+    public Object getLinearBuffer() {
+        return mLinearBuffer;
+    }
+
+    /**
+     * Returns whether the data is secure.
+     *
+     * @return {@code true} if the data is in secure area, and isn't mappable;
+     *         {@code false} otherwise.
+     */
+    public boolean getIsSecureMemory() {
+        return mIsSecureMemory;
+    }
+
+    /**
+     * Gets MPU sequence number of filtered data.
+     */
+    public int getMpuSequenceNumber() {
+        return mMpuSequenceNumber;
+    }
+
+    /**
+     * Returns whether the data is private.
+     *
+     * @return {@code true} if the data is in private; {@code false} otherwise.
+     */
+    public boolean getIsPrivateData() {
+        return mIsPrivateData;
+    }
+
+    /**
+     * Gets audio extra metadata.
+     */
+    @Nullable
+    public AudioDescriptor getExtraMetaData() {
+        return mExtraMetaData;
+    }
 }
diff --git a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
index dbd8c77..7f37994 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
@@ -16,11 +16,34 @@
 
 package android.media.tv.tuner.filter;
 
+import android.media.tv.tuner.Tuner.Filter;
+
 /**
- * MMPT record event.
+ * Filter event sent from {@link Filter} objects with MMTP type.
+ *
  * @hide
  */
 public class MmtpRecordEvent extends FilterEvent {
-    private int mScHevcIndexMask;
-    private long mByteNumber;
+    private final int mScHevcIndexMask;
+    private final long mByteNumber;
+
+    // This constructor is used by JNI code only
+    private MmtpRecordEvent(int scHevcIndexMask, long byteNumber) {
+        mScHevcIndexMask = scHevcIndexMask;
+        mByteNumber = byteNumber;
+    }
+
+    /**
+     * Gets indexes which can be tagged by NAL unit group in HEVC according to ISO/IEC 23008-2.
+     */
+    public int getScHevcIndexMask() {
+        return mScHevcIndexMask;
+    }
+
+    /**
+     * Gets the byte number from beginning of the filter's output.
+     */
+    public long getByteNumber() {
+        return mByteNumber;
+    }
 }
diff --git a/media/java/android/media/tv/tuner/filter/PesEvent.java b/media/java/android/media/tv/tuner/filter/PesEvent.java
index 16536e2..60251bf 100644
--- a/media/java/android/media/tv/tuner/filter/PesEvent.java
+++ b/media/java/android/media/tv/tuner/filter/PesEvent.java
@@ -16,12 +16,43 @@
 
 package android.media.tv.tuner.filter;
 
+import android.media.tv.tuner.Tuner.Filter;
+
 /**
- * PES event.
+ * Filter event sent from {@link Filter} objects with PES type.
+ *
  * @hide
  */
 public class PesEvent extends FilterEvent {
-    private int mStreamId;
-    private int mDataLength;
-    private int mMpuSequenceNumber;
+    private final int mStreamId;
+    private final int mDataLength;
+    private final int mMpuSequenceNumber;
+
+    // This constructor is used by JNI code only
+    private PesEvent(int streamId, int dataLength, int mpuSequenceNumber) {
+        mStreamId = streamId;
+        mDataLength = dataLength;
+        mMpuSequenceNumber = mpuSequenceNumber;
+    }
+
+    /**
+     * Gets stream ID.
+     */
+    public int getStreamId() {
+        return mStreamId;
+    }
+
+    /**
+     * Gets data size in bytes of filtered data.
+     */
+    public int getDataLength() {
+        return mDataLength;
+    }
+
+    /**
+     * Gets MPU sequence number of filtered data.
+     */
+    public int getMpuSequenceNumber() {
+        return mMpuSequenceNumber;
+    }
 }
diff --git a/media/java/android/media/tv/tuner/filter/PesSettings.java b/media/java/android/media/tv/tuner/filter/PesSettings.java
index f38abf1..bfa1f8c 100644
--- a/media/java/android/media/tv/tuner/filter/PesSettings.java
+++ b/media/java/android/media/tv/tuner/filter/PesSettings.java
@@ -17,6 +17,9 @@
 package android.media.tv.tuner.filter;
 
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
 import android.media.tv.tuner.TunerConstants;
 import android.media.tv.tuner.TunerUtils;
 import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
@@ -26,6 +29,7 @@
  *
  * @hide
  */
+@SystemApi
 public class PesSettings extends Settings {
     private final int mStreamId;
     private final boolean mIsRaw;
@@ -37,12 +41,32 @@
     }
 
     /**
+     * Gets stream ID.
+     */
+    public int getStreamId() {
+        return mStreamId;
+    }
+
+    /**
+     * Returns whether the data is raw.
+     *
+     * @return {@code true} if the data is raw. Filter sends onFilterStatus callback
+     * instead of onFilterEvent for raw data. {@code false} otherwise.
+     */
+    public boolean isRaw() {
+        return mIsRaw;
+    }
+
+    /**
      * Creates a builder for {@link PesSettings}.
      *
      * @param mainType the filter main type of the settings.
+     * @param context the context of the caller.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @NonNull
-    public static Builder newBuilder(@FilterType int mainType) {
+    public static Builder builder(@NonNull Context context, @FilterType int mainType) {
+        TunerUtils.checkTunerPermission(context);
         return new Builder(mainType);
     }
 
@@ -70,13 +94,13 @@
         }
 
         /**
-         * Sets whether it's raw.
+         * Sets whether the data is raw.
          *
          * @param isRaw {@code true} if the data is raw. Filter sends onFilterStatus callback
          * instead of onFilterEvent for raw data. {@code false} otherwise.
          */
         @NonNull
-        public Builder setIsRaw(boolean isRaw) {
+        public Builder setRaw(boolean isRaw) {
             mIsRaw = isRaw;
             return this;
         }
diff --git a/media/java/android/media/tv/tuner/filter/Settings.java b/media/java/android/media/tv/tuner/filter/Settings.java
index 146aca74..9155926 100644
--- a/media/java/android/media/tv/tuner/filter/Settings.java
+++ b/media/java/android/media/tv/tuner/filter/Settings.java
@@ -16,11 +16,14 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.SystemApi;
+
 /**
  * Settings for filters of different subtypes.
  *
  * @hide
  */
+@SystemApi
 public abstract class Settings {
     private final int mType;
 
diff --git a/media/java/android/media/tv/tuner/filter/TemiEvent.java b/media/java/android/media/tv/tuner/filter/TemiEvent.java
index 3841604..031fa5c 100644
--- a/media/java/android/media/tv/tuner/filter/TemiEvent.java
+++ b/media/java/android/media/tv/tuner/filter/TemiEvent.java
@@ -16,12 +16,46 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.NonNull;
+import android.media.tv.tuner.Tuner.Filter;
+
 /**
- * TEMI event.
+ * Filter event sent from {@link Filter} objects for Timed External Media Information (TEMI) data.
+ *
  * @hide
  */
 public class TemiEvent extends FilterEvent {
-    private long mPts;
-    private byte mDescrTag;
-    private byte[] mDescrData;
+    private final long mPts;
+    private final byte mDescrTag;
+    private final byte[] mDescrData;
+
+    // This constructor is used by JNI code only
+    private TemiEvent(long pts, byte descrTag, byte[] descrData) {
+        mPts = pts;
+        mDescrTag = descrTag;
+        mDescrData = descrData;
+    }
+
+
+    /**
+     * Gets PTS (Presentation Time Stamp) for audio or video frame.
+     */
+    public long getPts() {
+        return mPts;
+    }
+
+    /**
+     * Gets TEMI descriptor tag.
+     */
+    public byte getDescriptorTag() {
+        return mDescrTag;
+    }
+
+    /**
+     * Gets TEMI descriptor.
+     */
+    @NonNull
+    public byte[] getDescriptorData() {
+        return mDescrData;
+    }
 }
diff --git a/media/java/android/media/tv/tuner/TimeFilter.java b/media/java/android/media/tv/tuner/filter/TimeFilter.java
similarity index 98%
rename from media/java/android/media/tv/tuner/TimeFilter.java
rename to media/java/android/media/tv/tuner/filter/TimeFilter.java
index 8bd0d26..c975004 100644
--- a/media/java/android/media/tv/tuner/TimeFilter.java
+++ b/media/java/android/media/tv/tuner/filter/TimeFilter.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.tv.tuner;
+package android.media.tv.tuner.filter;
 
 import android.annotation.Nullable;
 import android.media.tv.tuner.TunerConstants.Result;
diff --git a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
index d0241b6..5c38cfa 100644
--- a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
@@ -17,12 +17,18 @@
 package android.media.tv.tuner.filter;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.media.tv.tuner.TunerUtils;
 
 /**
  * Filter configuration for a TS filter.
  *
  * @hide
  */
+@SystemApi
 public class TsFilterConfiguration extends FilterConfiguration {
     private final int mTpid;
 
@@ -37,10 +43,28 @@
     }
 
     /**
-     * Creates a builder for {@link TsFilterConfiguration}.
+     * Gets the {@link Settings} object of this filter configuration.
      */
+    @Nullable
+    public Settings getSettings() {
+        return mSettings;
+    }
+    /**
+     * Gets Tag Protocol ID.
+     */
+    public int getTpid() {
+        return mTpid;
+    }
+
+    /**
+     * Creates a builder for {@link TsFilterConfiguration}.
+     *
+     * @param context the context of the caller.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @NonNull
-    public static Builder newBuilder() {
+    public static Builder builder(@NonNull Context context) {
+        TunerUtils.checkTunerPermission(context);
         return new Builder();
     }
 
@@ -51,6 +75,9 @@
         private Settings mSettings;
         private int mTpid;
 
+        private Builder() {
+        }
+
         /**
          * Sets filter settings.
          *
diff --git a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
index 875b5bd..fa4dd72 100644
--- a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
@@ -16,12 +16,84 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.IntDef;
+import android.media.tv.tuner.Tuner.Filter;
+import android.media.tv.tuner.TunerConstants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
- * TS record event.
+ * Filter event sent from {@link Filter} objects for TS record data.
+ *
  * @hide
  */
 public class TsRecordEvent extends FilterEvent {
-    private int mTpid;
-    private int mIndexMask;
-    private long mByteNumber;
+    /**
+     * @hide
+     */
+    @IntDef(flag = true, value = {
+            TunerConstants.TS_INDEX_FIRST_PACKET,
+            TunerConstants.TS_INDEX_PAYLOAD_UNIT_START_INDICATOR,
+            TunerConstants.TS_INDEX_CHANGE_TO_NOT_SCRAMBLED,
+            TunerConstants.TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED,
+            TunerConstants.TS_INDEX_CHANGE_TO_ODD_SCRAMBLED,
+            TunerConstants.TS_INDEX_DISCONTINUITY_INDICATOR,
+            TunerConstants.TS_INDEX_RANDOM_ACCESS_INDICATOR,
+            TunerConstants.TS_INDEX_PRIORITY_INDICATOR,
+            TunerConstants.TS_INDEX_PCR_FLAG,
+            TunerConstants.TS_INDEX_OPCR_FLAG,
+            TunerConstants.TS_INDEX_SPLICING_POINT_FLAG,
+            TunerConstants.TS_INDEX_PRIVATE_DATA,
+            TunerConstants.TS_INDEX_ADAPTATION_EXTENSION_FLAG,
+            TunerConstants.SC_INDEX_I_FRAME,
+            TunerConstants.SC_INDEX_P_FRAME,
+            TunerConstants.SC_INDEX_B_FRAME,
+            TunerConstants.SC_INDEX_SEQUENCE,
+            TunerConstants.SC_HEVC_INDEX_SPS,
+            TunerConstants.SC_HEVC_INDEX_AUD,
+            TunerConstants.SC_HEVC_INDEX_SLICE_CE_BLA_W_LP,
+            TunerConstants.SC_HEVC_INDEX_SLICE_BLA_W_RADL,
+            TunerConstants.SC_HEVC_INDEX_SLICE_BLA_N_LP,
+            TunerConstants.SC_HEVC_INDEX_SLICE_IDR_W_RADL,
+            TunerConstants.SC_HEVC_INDEX_SLICE_IDR_N_LP,
+            TunerConstants.SC_HEVC_INDEX_SLICE_TRAIL_CRA,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IndexMask {}
+
+    private final int mPid;
+    private final int mIndexMask;
+    private final long mByteNumber;
+
+    // This constructor is used by JNI code only
+    private TsRecordEvent(int pid, int indexMask, long byteNumber) {
+        mPid = pid;
+        mIndexMask = indexMask;
+        mByteNumber = byteNumber;
+    }
+
+    /**
+     * Gets packet ID.
+     */
+    public int getTpid() {
+        return mPid;
+    }
+
+    /**
+     * Gets index mask.
+     *
+     * <p>The index type is one of TS, SC, and SC-HEVC, and is set when configuring the filter.
+     */
+    @IndexMask
+    public int getIndexMask() {
+        return mIndexMask;
+    }
+
+    /**
+     * Gets the byte number from beginning of the filter's output.
+     */
+    public long getByteNumber() {
+        return mByteNumber;
+    }
 }
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 8c0273b..221783b 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -16,14 +16,13 @@
 
 package com.android.mediarouteprovider.example;
 
-import static android.media.MediaRoute2Info.DEVICE_TYPE_SPEAKER;
-import static android.media.MediaRoute2Info.DEVICE_TYPE_TV;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
 
 import android.content.Intent;
 import android.media.MediaRoute2Info;
-import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRoute2ProviderService;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
 import android.os.IBinder;
 import android.text.TextUtils;
 
@@ -46,8 +45,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_TYPE = "route_special_type";
-    public static final String ROUTE_NAME_SPECIAL_TYPE = "Special Type Route";
+    public static final String ROUTE_ID_SPECIAL_FEATURE = "route_special_feature";
+    public static final String ROUTE_NAME_SPECIAL_FEATURE = "Special Feature Route";
 
     public static final int VOLUME_MAX = 100;
     public static final String ROUTE_ID_FIXED_VOLUME = "route_fixed_volume";
@@ -58,49 +57,49 @@
     public static final String ACTION_REMOVE_ROUTE =
             "com.android.mediarouteprovider.action_remove_route";
 
-    public static final String TYPE_SAMPLE =
-            "com.android.mediarouteprovider.TYPE_SAMPLE";
-    public static final String TYPE_SPECIAL =
-            "com.android.mediarouteprovider.TYPE_SPECIAL";
+    public static final String FEATURE_SAMPLE =
+            "com.android.mediarouteprovider.FEATURE_SAMPLE";
+    public static final String FEATURE_SPECIAL =
+            "com.android.mediarouteprovider.FEATURE_SPECIAL";
 
     Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
-    Map<String, String> mRouteSessionMap = new HashMap<>();
+    Map<String, String> mRouteIdToSessionId = new HashMap<>();
     private int mNextSessionId = 1000;
 
     private void initializeRoutes() {
         MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1)
-                .addRouteType(TYPE_SAMPLE)
-                .setDeviceType(DEVICE_TYPE_TV)
+                .addFeature(FEATURE_SAMPLE)
+                .setDeviceType(DEVICE_TYPE_REMOTE_TV)
                 .build();
         MediaRoute2Info route2 = new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2)
-                .addRouteType(TYPE_SAMPLE)
-                .setDeviceType(DEVICE_TYPE_SPEAKER)
+                .addFeature(FEATURE_SAMPLE)
+                .setDeviceType(DEVICE_TYPE_REMOTE_SPEAKER)
                 .build();
         MediaRoute2Info route3 = new MediaRoute2Info.Builder(
                 ROUTE_ID3_SESSION_CREATION_FAILED, ROUTE_NAME3)
-                .addRouteType(TYPE_SAMPLE)
+                .addFeature(FEATURE_SAMPLE)
                 .build();
         MediaRoute2Info route4 = new MediaRoute2Info.Builder(
                 ROUTE_ID4_TO_SELECT_AND_DESELECT, ROUTE_NAME4)
-                .addRouteType(TYPE_SAMPLE)
+                .addFeature(FEATURE_SAMPLE)
                 .build();
         MediaRoute2Info route5 = new MediaRoute2Info.Builder(
                 ROUTE_ID5_TO_TRANSFER_TO, ROUTE_NAME5)
-                .addRouteType(TYPE_SAMPLE)
+                .addFeature(FEATURE_SAMPLE)
                 .build();
         MediaRoute2Info routeSpecial =
-                new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_TYPE, ROUTE_NAME_SPECIAL_TYPE)
-                        .addRouteType(TYPE_SAMPLE)
-                        .addRouteType(TYPE_SPECIAL)
+                new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_FEATURE, ROUTE_NAME_SPECIAL_FEATURE)
+                        .addFeature(FEATURE_SAMPLE)
+                        .addFeature(FEATURE_SPECIAL)
                         .build();
         MediaRoute2Info fixedVolumeRoute =
                 new MediaRoute2Info.Builder(ROUTE_ID_FIXED_VOLUME, ROUTE_NAME_FIXED_VOLUME)
-                        .addRouteType(TYPE_SAMPLE)
+                        .addFeature(FEATURE_SAMPLE)
                         .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_FIXED)
                         .build();
         MediaRoute2Info variableVolumeRoute =
                 new MediaRoute2Info.Builder(ROUTE_ID_VARIABLE_VOLUME, ROUTE_NAME_VARIABLE_VOLUME)
-                        .addRouteType(TYPE_SAMPLE)
+                        .addFeature(FEATURE_SAMPLE)
                         .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
                         .setVolumeMax(VOLUME_MAX)
                         .build();
@@ -154,6 +153,7 @@
 
     @Override
     public void onUpdateVolume(String routeId, int delta) {
+        android.util.Log.d(TAG, "onUpdateVolume routeId= " + routeId + "delta=" + delta);
         MediaRoute2Info route = mRoutes.get(routeId);
         if (route == null) {
             return;
@@ -167,7 +167,7 @@
     }
 
     @Override
-    public void onCreateSession(String packageName, String routeId, String routeType,
+    public void onCreateSession(String packageName, String routeId, String routeFeature,
             long requestId) {
         MediaRoute2Info route = mRoutes.get(routeId);
         if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) {
@@ -183,10 +183,10 @@
         mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
                 .setClientPackageName(packageName)
                 .build());
-        mRouteSessionMap.put(routeId, sessionId);
+        mRouteIdToSessionId.put(routeId, sessionId);
 
-        RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
-                sessionId, packageName, routeType)
+        RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
+                sessionId, packageName, routeFeature)
                 .addSelectedRoute(routeId)
                 .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                 .addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO)
@@ -196,9 +196,9 @@
     }
 
     @Override
-    public void onDestroySession(String sessionId, RouteSessionInfo lastSessionInfo) {
+    public void onDestroySession(String sessionId, RoutingSessionInfo lastSessionInfo) {
         for (String routeId : lastSessionInfo.getSelectedRoutes()) {
-            mRouteSessionMap.remove(routeId);
+            mRouteIdToSessionId.remove(routeId);
             MediaRoute2Info route = mRoutes.get(routeId);
             if (route != null) {
                 mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
@@ -210,7 +210,7 @@
 
     @Override
     public void onSelectRoute(String sessionId, String routeId) {
-        RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
+        RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
         MediaRoute2Info route = mRoutes.get(routeId);
         if (route == null || sessionInfo == null) {
             return;
@@ -220,9 +220,9 @@
         mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
                 .setClientPackageName(sessionInfo.getClientPackageName())
                 .build());
-        mRouteSessionMap.put(routeId, sessionId);
+        mRouteIdToSessionId.put(routeId, sessionId);
 
-        RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+        RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
                 .addSelectedRoute(routeId)
                 .removeSelectableRoute(routeId)
                 .addDeselectableRoute(routeId)
@@ -233,18 +233,25 @@
 
     @Override
     public void onDeselectRoute(String sessionId, String routeId) {
-        RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
+        RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
         MediaRoute2Info route = mRoutes.get(routeId);
 
-        mRouteSessionMap.remove(routeId);
-        if (sessionInfo == null || route == null) {
+        if (sessionInfo == null || route == null
+                || !sessionInfo.getSelectedRoutes().contains(routeId)) {
             return;
         }
+
+        mRouteIdToSessionId.remove(routeId);
         mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
                 .setClientPackageName(null)
                 .build());
 
-        RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+        if (sessionInfo.getSelectedRoutes().size() == 1) {
+            releaseSession(sessionId);
+            return;
+        }
+
+        RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
                 .removeSelectedRoute(routeId)
                 .addSelectableRoute(routeId)
                 .removeDeselectableRoute(routeId)
@@ -255,8 +262,8 @@
 
     @Override
     public void onTransferToRoute(String sessionId, String routeId) {
-        RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
-        RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+        RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
+        RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
                 .clearSelectedRoutes()
                 .addSelectedRoute(routeId)
                 .removeDeselectableRoute(routeId)
@@ -267,18 +274,15 @@
     }
 
     void maybeDeselectRoute(String routeId) {
-        if (!mRouteSessionMap.containsKey(routeId)) {
+        if (!mRouteIdToSessionId.containsKey(routeId)) {
             return;
         }
 
-        String sessionId = mRouteSessionMap.get(routeId);
+        String sessionId = mRouteIdToSessionId.get(routeId);
         onDeselectRoute(sessionId, routeId);
     }
 
     void publishRoutes() {
-        MediaRoute2ProviderInfo info = new MediaRoute2ProviderInfo.Builder()
-                .addRoutes(mRoutes.values())
-                .build();
-        updateProviderInfo(info);
+        notifyRoutes(mRoutes.values());
     }
 }
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index ce4bb8e..007229a 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -18,23 +18,23 @@
 
 import static android.media.MediaRoute2Info.CONNECTION_STATE_CONNECTED;
 import static android.media.MediaRoute2Info.CONNECTION_STATE_CONNECTING;
-import static android.media.MediaRoute2Info.DEVICE_TYPE_SPEAKER;
-import static android.media.MediaRoute2Info.DEVICE_TYPE_TV;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
 import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
 import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
 
+import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURES_ALL;
+import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURES_SPECIAL;
+import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURE_SAMPLE;
+import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURE_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_TYPE;
+import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_SPECIAL_FEATURE;
 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;
@@ -48,10 +48,10 @@
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2;
 import android.media.MediaRouter2.RouteCallback;
-import android.media.MediaRouter2.RouteSessionController;
+import android.media.MediaRouter2.RoutingController;
 import android.media.MediaRouter2.SessionCallback;
-import android.media.RouteDiscoveryRequest;
-import android.media.RouteSessionInfo;
+import android.media.RouteDiscoveryPreference;
+import android.media.RoutingSessionInfo;
 import android.net.Uri;
 import android.os.Parcel;
 import android.support.test.InstrumentationRegistry;
@@ -100,10 +100,10 @@
      */
     @Test
     public void testGetRoutes() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(TYPES_SPECIAL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(FEATURES_SPECIAL);
 
         assertEquals(1, routes.size());
-        assertNotNull(routes.get(ROUTE_ID_SPECIAL_TYPE));
+        assertNotNull(routes.get(ROUTE_ID_SPECIAL_FEATURE));
     }
 
     @Test
@@ -115,9 +115,9 @@
                 .setIconUri(new Uri.Builder().path("icon").build())
                 .setVolume(5)
                 .setVolumeMax(20)
-                .addRouteType(TYPE_SAMPLE)
+                .addFeature(FEATURE_SAMPLE)
                 .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
-                .setDeviceType(DEVICE_TYPE_SPEAKER)
+                .setDeviceType(DEVICE_TYPE_REMOTE_SPEAKER)
                 .build();
 
         MediaRoute2Info routeInfoRebuilt = new MediaRoute2Info.Builder(routeInfo).build();
@@ -138,21 +138,13 @@
                 .setClientPackageName("com.android.mediaroutertest")
                 .setConnectionState(CONNECTION_STATE_CONNECTING)
                 .setIconUri(new Uri.Builder().path("icon").build())
-                .addRouteType(TYPE_SAMPLE)
+                .addFeature(FEATURE_SAMPLE)
                 .setVolume(5)
                 .setVolumeMax(20)
                 .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
-                .setDeviceType(DEVICE_TYPE_SPEAKER)
+                .setDeviceType(DEVICE_TYPE_REMOTE_SPEAKER)
                 .build();
 
-        MediaRoute2Info routeId = new MediaRoute2Info.Builder(route)
-                .setId("another id").build();
-        assertNotEquals(route, routeId);
-
-        MediaRoute2Info routeName = new MediaRoute2Info.Builder(route)
-                .setName("another name").build();
-        assertNotEquals(route, routeName);
-
         MediaRoute2Info routeDescription = new MediaRoute2Info.Builder(route)
                 .setDescription("another description").build();
         assertNotEquals(route, routeDescription);
@@ -170,7 +162,7 @@
         assertNotEquals(route, routeClient);
 
         MediaRoute2Info routeType = new MediaRoute2Info.Builder(route)
-                .addRouteType(TYPE_SPECIAL).build();
+                .addFeature(FEATURE_SPECIAL).build();
         assertNotEquals(route, routeType);
 
         MediaRoute2Info routeVolume = new MediaRoute2Info.Builder(route)
@@ -186,13 +178,13 @@
         assertNotEquals(route, routeVolumeHandling);
 
         MediaRoute2Info routeDeviceType = new MediaRoute2Info.Builder(route)
-                .setVolume(DEVICE_TYPE_TV).build();
+                .setVolume(DEVICE_TYPE_REMOTE_TV).build();
         assertNotEquals(route, routeDeviceType);
     }
 
     @Test
     public void testControlVolumeWithRouter() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(TYPES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(FEATURES_ALL);
 
         MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
         assertNotNull(volRoute);
@@ -204,13 +196,13 @@
                 () -> mRouter2.requestUpdateVolume(volRoute, deltaVolume),
                 ROUTE_ID_VARIABLE_VOLUME,
                 (route -> route.getVolume() == originalVolume + deltaVolume),
-                TYPES_ALL);
+                FEATURES_ALL);
 
         awaitOnRouteChanged(
                 () -> mRouter2.requestSetVolume(volRoute, originalVolume),
                 ROUTE_ID_VARIABLE_VOLUME,
                 (route -> route.getVolume() == originalVolume),
-                TYPES_ALL);
+                FEATURES_ALL);
     }
 
     @Test
@@ -237,13 +229,13 @@
     @Test
     public void testRequestCreateSessionWithInvalidArguments() {
         MediaRoute2Info route = new MediaRoute2Info.Builder("id", "name").build();
-        String routeType = "routeType";
+        String routeFeature = "routeFeature";
 
         // Tests null route
         assertThrows(NullPointerException.class,
-                () -> mRouter2.requestCreateSession(null, routeType));
+                () -> mRouter2.requestCreateSession(null, routeFeature));
 
-        // Tests null or empty route type
+        // Tests null or empty route feature
         assertThrows(IllegalArgumentException.class,
                 () -> mRouter2.requestCreateSession(route, null));
         assertThrows(IllegalArgumentException.class,
@@ -252,42 +244,42 @@
 
     @Test
     public void testRequestCreateSessionSuccess() throws Exception {
-        final List<String> sampleRouteType = new ArrayList<>();
-        sampleRouteType.add(TYPE_SAMPLE);
+        final List<String> sampleRouteFeature = new ArrayList<>();
+        sampleRouteFeature.add(FEATURE_SAMPLE);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteFeature);
         MediaRoute2Info route = routes.get(ROUTE_ID1);
         assertNotNull(route);
 
         final CountDownLatch successLatch = new CountDownLatch(1);
         final CountDownLatch failureLatch = new CountDownLatch(1);
-        final List<RouteSessionController> controllers = new ArrayList<>();
+        final List<RoutingController> controllers = new ArrayList<>();
 
         // Create session with this route
         SessionCallback sessionCallback = new SessionCallback() {
             @Override
-            public void onSessionCreated(RouteSessionController controller) {
+            public void onSessionCreated(RoutingController controller) {
                 assertNotNull(controller);
                 assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1));
-                assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType()));
+                assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
                 controllers.add(controller);
                 successLatch.countDown();
             }
 
             @Override
             public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedRouteType) {
+                    String requestedRouteFeature) {
                 failureLatch.countDown();
             }
         };
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route, TYPE_SAMPLE);
+            mRouter2.requestCreateSession(route, FEATURE_SAMPLE);
             assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // onSessionCreationFailed should not be called.
@@ -302,7 +294,7 @@
     @Test
     public void testRequestCreateSessionFailure() throws Exception {
         final List<String> sampleRouteType = new ArrayList<>();
-        sampleRouteType.add(TYPE_SAMPLE);
+        sampleRouteType.add(FEATURE_SAMPLE);
 
         Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info route = routes.get(ROUTE_ID3_SESSION_CREATION_FAILED);
@@ -310,32 +302,32 @@
 
         final CountDownLatch successLatch = new CountDownLatch(1);
         final CountDownLatch failureLatch = new CountDownLatch(1);
-        final List<RouteSessionController> controllers = new ArrayList<>();
+        final List<RoutingController> controllers = new ArrayList<>();
 
         // Create session with this route
         SessionCallback sessionCallback = new SessionCallback() {
             @Override
-            public void onSessionCreated(RouteSessionController controller) {
+            public void onSessionCreated(RoutingController controller) {
                 controllers.add(controller);
                 successLatch.countDown();
             }
 
             @Override
             public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedRouteType) {
+                    String requestedRouteFeature) {
                 assertEquals(route, requestedRoute);
-                assertTrue(TextUtils.equals(TYPE_SAMPLE, requestedRouteType));
+                assertTrue(TextUtils.equals(FEATURE_SAMPLE, requestedRouteFeature));
                 failureLatch.countDown();
             }
         };
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route, TYPE_SAMPLE);
+            mRouter2.requestCreateSession(route, FEATURE_SAMPLE);
             assertTrue(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // onSessionCreated should not be called.
@@ -350,23 +342,23 @@
     @Test
     public void testRequestCreateSessionMultipleSessions() throws Exception {
         final List<String> sampleRouteType = new ArrayList<>();
-        sampleRouteType.add(TYPE_SAMPLE);
+        sampleRouteType.add(FEATURE_SAMPLE);
 
         final CountDownLatch successLatch = new CountDownLatch(2);
         final CountDownLatch failureLatch = new CountDownLatch(1);
-        final List<RouteSessionController> createdControllers = new ArrayList<>();
+        final List<RoutingController> createdControllers = new ArrayList<>();
 
         // Create session with this route
         SessionCallback sessionCallback = new SessionCallback() {
             @Override
-            public void onSessionCreated(RouteSessionController controller) {
+            public void onSessionCreated(RoutingController controller) {
                 createdControllers.add(controller);
                 successLatch.countDown();
             }
 
             @Override
             public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedRouteType) {
+                    String requestedRouteFeature) {
                 failureLatch.countDown();
             }
         };
@@ -379,12 +371,12 @@
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route1, TYPE_SAMPLE);
-            mRouter2.requestCreateSession(route2, TYPE_SAMPLE);
+            mRouter2.requestCreateSession(route1, FEATURE_SAMPLE);
+            mRouter2.requestCreateSession(route2, FEATURE_SAMPLE);
             assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             // onSessionCreationFailed should not be called.
@@ -392,14 +384,14 @@
 
             // Created controllers should have proper info
             assertEquals(2, createdControllers.size());
-            RouteSessionController controller1 = createdControllers.get(0);
-            RouteSessionController controller2 = createdControllers.get(1);
+            RoutingController controller1 = createdControllers.get(0);
+            RoutingController controller2 = createdControllers.get(1);
 
             assertNotEquals(controller1.getSessionId(), controller2.getSessionId());
             assertTrue(createRouteMap(controller1.getSelectedRoutes()).containsKey(ROUTE_ID1));
             assertTrue(createRouteMap(controller2.getSelectedRoutes()).containsKey(ROUTE_ID2));
-            assertTrue(TextUtils.equals(TYPE_SAMPLE, controller1.getRouteType()));
-            assertTrue(TextUtils.equals(TYPE_SAMPLE, controller2.getRouteType()));
+            assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller1.getRouteFeature()));
+            assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller2.getRouteFeature()));
 
         } finally {
             releaseControllers(createdControllers);
@@ -411,7 +403,7 @@
     @Test
     public void testSessionCallbackIsNotCalledAfterUnregistered() throws Exception {
         final List<String> sampleRouteType = new ArrayList<>();
-        sampleRouteType.add(TYPE_SAMPLE);
+        sampleRouteType.add(FEATURE_SAMPLE);
 
         Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info route = routes.get(ROUTE_ID1);
@@ -419,30 +411,30 @@
 
         final CountDownLatch successLatch = new CountDownLatch(1);
         final CountDownLatch failureLatch = new CountDownLatch(1);
-        final List<RouteSessionController> controllers = new ArrayList<>();
+        final List<RoutingController> controllers = new ArrayList<>();
 
         // Create session with this route
         SessionCallback sessionCallback = new SessionCallback() {
             @Override
-            public void onSessionCreated(RouteSessionController controller) {
+            public void onSessionCreated(RoutingController controller) {
                 controllers.add(controller);
                 successLatch.countDown();
             }
 
             @Override
             public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
-                    String requestedRouteType) {
+                    String requestedRouteFeature) {
                 failureLatch.countDown();
             }
         };
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(route, TYPE_SAMPLE);
+            mRouter2.requestCreateSession(route, FEATURE_SAMPLE);
 
             // Unregisters session callback
             mRouter2.unregisterSessionCallback(sessionCallback);
@@ -459,9 +451,9 @@
 
     // TODO: Add tests for illegal inputs if needed (e.g. selecting already selected route)
     @Test
-    public void testRouteSessionControllerSelectAndDeselectRoute() throws Exception {
+    public void testRoutingControllerSelectAndDeselectRoute() throws Exception {
         final List<String> sampleRouteType = new ArrayList<>();
-        sampleRouteType.add(TYPE_SAMPLE);
+        sampleRouteType.add(FEATURE_SAMPLE);
 
         Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
@@ -470,22 +462,22 @@
         final CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
         final CountDownLatch onSessionInfoChangedLatchForSelect = new CountDownLatch(1);
         final CountDownLatch onSessionInfoChangedLatchForDeselect = new CountDownLatch(1);
-        final List<RouteSessionController> controllers = new ArrayList<>();
+        final List<RoutingController> controllers = new ArrayList<>();
 
         // Create session with ROUTE_ID1
         SessionCallback sessionCallback = new SessionCallback() {
             @Override
-            public void onSessionCreated(RouteSessionController controller) {
+            public void onSessionCreated(RoutingController controller) {
                 assertNotNull(controller);
                 assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
-                assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType()));
+                assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
                 controllers.add(controller);
                 onSessionCreatedLatch.countDown();
             }
 
             @Override
-            public void onSessionInfoChanged(RouteSessionController controller,
-                    RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
+            public void onSessionInfoChanged(RoutingController controller,
+                    RoutingSessionInfo oldInfo, RoutingSessionInfo newInfo) {
                 if (onSessionCreatedLatch.getCount() != 0
                         || !TextUtils.equals(
                                 controllers.get(0).getSessionId(), controller.getSessionId())) {
@@ -527,15 +519,15 @@
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(routeToCreateSessionWith, TYPE_SAMPLE);
+            mRouter2.requestCreateSession(routeToCreateSessionWith, FEATURE_SAMPLE);
             assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             assertEquals(1, controllers.size());
-            RouteSessionController controller = controllers.get(0);
+            RoutingController controller = controllers.get(0);
             assertTrue(getRouteIds(controller.getSelectableRoutes())
                     .contains(ROUTE_ID4_TO_SELECT_AND_DESELECT));
 
@@ -558,9 +550,9 @@
     }
 
     @Test
-    public void testRouteSessionControllerTransferToRoute() throws Exception {
+    public void testRoutingControllerTransferToRoute() throws Exception {
         final List<String> sampleRouteType = new ArrayList<>();
-        sampleRouteType.add(TYPE_SAMPLE);
+        sampleRouteType.add(FEATURE_SAMPLE);
 
         Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
@@ -568,26 +560,22 @@
 
         final CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
         final CountDownLatch onSessionInfoChangedLatch = new CountDownLatch(1);
-        final List<RouteSessionController> controllers = new ArrayList<>();
+        final List<RoutingController> controllers = new ArrayList<>();
 
         // Create session with ROUTE_ID1
         SessionCallback sessionCallback = new SessionCallback() {
             @Override
-            public void onSessionCreated(RouteSessionController controller) {
+            public void onSessionCreated(RoutingController 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(TYPE_SAMPLE, controller.getRouteType()));
+                assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
                 controllers.add(controller);
                 onSessionCreatedLatch.countDown();
             }
 
             @Override
-            public void onSessionInfoChanged(RouteSessionController controller,
-                    RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
+            public void onSessionInfoChanged(RoutingController controller,
+                    RoutingSessionInfo oldInfo, RoutingSessionInfo newInfo) {
                 if (onSessionCreatedLatch.getCount() != 0
                         || !TextUtils.equals(
                                 controllers.get(0).getSessionId(), controller.getSessionId())) {
@@ -613,15 +601,15 @@
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(routeToCreateSessionWith, TYPE_SAMPLE);
+            mRouter2.requestCreateSession(routeToCreateSessionWith, FEATURE_SAMPLE);
             assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             assertEquals(1, controllers.size());
-            RouteSessionController controller = controllers.get(0);
+            RoutingController controller = controllers.get(0);
             assertTrue(getRouteIds(controller.getTransferrableRoutes())
                     .contains(ROUTE_ID5_TO_TRANSFER_TO));
 
@@ -641,9 +629,9 @@
     // TODO: Add tests for onSessionReleased() call.
 
     @Test
-    public void testRouteSessionControllerReleaseShouldIgnoreTransferTo() throws Exception {
+    public void testRoutingControllerReleaseShouldIgnoreTransferTo() throws Exception {
         final List<String> sampleRouteType = new ArrayList<>();
-        sampleRouteType.add(TYPE_SAMPLE);
+        sampleRouteType.add(FEATURE_SAMPLE);
 
         Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
         MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
@@ -651,22 +639,22 @@
 
         final CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
         final CountDownLatch onSessionInfoChangedLatch = new CountDownLatch(1);
-        final List<RouteSessionController> controllers = new ArrayList<>();
+        final List<RoutingController> controllers = new ArrayList<>();
 
         // Create session with ROUTE_ID1
         SessionCallback sessionCallback = new SessionCallback() {
             @Override
-            public void onSessionCreated(RouteSessionController controller) {
+            public void onSessionCreated(RoutingController controller) {
                 assertNotNull(controller);
                 assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
-                assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType()));
+                assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
                 controllers.add(controller);
                 onSessionCreatedLatch.countDown();
             }
 
             @Override
-            public void onSessionInfoChanged(RouteSessionController controller,
-                    RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
+            public void onSessionInfoChanged(RoutingController controller,
+                    RoutingSessionInfo oldInfo, RoutingSessionInfo newInfo) {
                 if (onSessionCreatedLatch.getCount() != 0
                         || !TextUtils.equals(
                                 controllers.get(0).getSessionId(), controller.getSessionId())) {
@@ -678,15 +666,15 @@
 
         // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
         RouteCallback routeCallback = new RouteCallback();
-        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
 
         try {
             mRouter2.registerSessionCallback(mExecutor, sessionCallback);
-            mRouter2.requestCreateSession(routeToCreateSessionWith, TYPE_SAMPLE);
+            mRouter2.requestCreateSession(routeToCreateSessionWith, FEATURE_SAMPLE);
             assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
             assertEquals(1, controllers.size());
-            RouteSessionController controller = controllers.get(0);
+            RoutingController controller = controllers.get(0);
             assertTrue(getRouteIds(controller.getTransferrableRoutes())
                     .contains(ROUTE_ID5_TO_TRANSFER_TO));
 
@@ -735,7 +723,7 @@
         };
 
         mRouter2.registerRouteCallback(mExecutor, routeCallback,
-                new RouteDiscoveryRequest.Builder(routeTypes, true).build());
+                new RouteDiscoveryPreference.Builder(routeTypes, true).build());
         try {
             latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
             return createRouteMap(mRouter2.getRoutes());
@@ -744,8 +732,8 @@
         }
     }
 
-    static void releaseControllers(@NonNull List<RouteSessionController> controllers) {
-        for (RouteSessionController controller : controllers) {
+    static void releaseControllers(@NonNull List<RoutingController> controllers) {
+        for (RoutingController controller : controllers) {
             controller.release();
         }
     }
@@ -775,7 +763,7 @@
             }
         };
         mRouter2.registerRouteCallback(mExecutor, routeCallback,
-                new RouteDiscoveryRequest.Builder(routeTypes, true).build());
+                new RouteDiscoveryPreference.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 9ff9177c..ae15b91 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -31,7 +31,7 @@
 import android.media.MediaRouter2.RouteCallback;
 import android.media.MediaRouter2.SessionCallback;
 import android.media.MediaRouter2Manager;
-import android.media.RouteDiscoveryRequest;
+import android.media.RouteDiscoveryPreference;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -76,9 +76,9 @@
             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_TYPE =
-            SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_special_type";
-    public static final String ROUTE_NAME_SPECIAL_TYPE = "Special Type Route";
+    public static final String ROUTE_ID_SPECIAL_FEATURE =
+            SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_special_feature";
+    public static final String ROUTE_NAME_SPECIAL_FEATURE = "Special Feature Route";
 
     public static final String SYSTEM_PROVIDER_ID =
             "com.android.server.media/.SystemMediaRoute2Provider";
@@ -94,12 +94,12 @@
     public static final String ACTION_REMOVE_ROUTE =
             "com.android.mediarouteprovider.action_remove_route";
 
-    public static final String TYPE_SAMPLE =
-            "com.android.mediarouteprovider.TYPE_SAMPLE";
-    public static final String TYPE_SPECIAL =
-            "com.android.mediarouteprovider.TYPE_SPECIAL";
+    public static final String FEATURE_SAMPLE =
+            "com.android.mediarouteprovider.FEATURE_SAMPLE";
+    public static final String FEATURE_SPECIAL =
+            "com.android.mediarouteprovider.FEATURE_SPECIAL";
 
-    private static final String TYPE_LIVE_AUDIO = "android.media.intent.route.TYPE_LIVE_AUDIO";
+    private static final String FEATURE_LIVE_AUDIO = "android.media.intent.route.LIVE_AUDIO";
 
     private static final int TIMEOUT_MS = 5000;
 
@@ -113,18 +113,18 @@
     private final List<RouteCallback> mRouteCallbacks = new ArrayList<>();
     private final List<SessionCallback> mSessionCallbacks = 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<>();
+    public static final List<String> FEATURES_ALL = new ArrayList();
+    public static final List<String> FEATURES_SPECIAL = new ArrayList();
+    private static final List<String> FEATURES_LIVE_AUDIO = new ArrayList<>();
 
     static {
-        TYPES_ALL.add(TYPE_SAMPLE);
-        TYPES_ALL.add(TYPE_SPECIAL);
-        TYPES_ALL.add(TYPE_LIVE_AUDIO);
+        FEATURES_ALL.add(FEATURE_SAMPLE);
+        FEATURES_ALL.add(FEATURE_SPECIAL);
+        FEATURES_ALL.add(FEATURE_LIVE_AUDIO);
 
-        TYPES_SPECIAL.add(TYPE_SPECIAL);
+        FEATURES_SPECIAL.add(FEATURE_SPECIAL);
 
-        TYPES_LIVE_AUDIO.add(TYPE_LIVE_AUDIO);
+        FEATURES_LIVE_AUDIO.add(FEATURE_LIVE_AUDIO);
     }
 
     @Before
@@ -181,7 +181,7 @@
     @Test
     public void testOnRoutesRemoved() throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
 
         addRouterCallback(new RouteCallback());
         addManagerCallback(new MediaRouter2Manager.Callback() {
@@ -203,14 +203,14 @@
     }
 
     /**
-     * Tests if we get proper routes for application that has special route type.
+     * Tests if we get proper routes for application that has special route feature.
      */
     @Test
-    public void testRouteType() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_SPECIAL);
+    public void testRouteFeatures() throws Exception {
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_SPECIAL);
 
         assertEquals(1, routes.size());
-        assertNotNull(routes.get(ROUTE_ID_SPECIAL_TYPE));
+        assertNotNull(routes.get(ROUTE_ID_SPECIAL_FEATURE));
     }
 
     /**
@@ -219,7 +219,7 @@
      */
     @Test
     public void testRouterOnSessionCreated() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
 
         CountDownLatch latch = new CountDownLatch(1);
 
@@ -228,7 +228,7 @@
         addRouterCallback(new MediaRouter2.RouteCallback());
         addSessionCallback(new SessionCallback() {
             @Override
-            public void onSessionCreated(MediaRouter2.RouteSessionController controller) {
+            public void onSessionCreated(MediaRouter2.RoutingController controller) {
                 if (createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1)) {
                     latch.countDown();
                 }
@@ -255,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(TYPES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
 
         addRouterCallback(new RouteCallback());
         addManagerCallback(new MediaRouter2Manager.Callback() {
@@ -285,7 +285,7 @@
     public void testGetActiveRoutes() throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
         addRouterCallback(new RouteCallback());
         addManagerCallback(new MediaRouter2Manager.Callback() {
             @Override
@@ -321,7 +321,7 @@
     @Test
     @Ignore("TODO: enable when session is released")
     public void testSingleProviderSelect() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
         addRouterCallback(new RouteCallback());
 
         awaitOnRouteChangedManager(
@@ -346,7 +346,7 @@
 
     @Test
     public void testControlVolumeWithManager() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
         MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
 
         int originalVolume = volRoute.getVolume();
@@ -365,7 +365,7 @@
 
     @Test
     public void testVolumeHandling() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
 
         MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME);
         MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
@@ -375,11 +375,11 @@
         assertEquals(VOLUME_MAX, variableVolumeRoute.getVolumeMax());
     }
 
-    Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> routeTypes)
+    Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> routeFeatures)
             throws Exception {
         CountDownLatch latch = new CountDownLatch(2);
 
-        // A dummy callback is required to send route type info.
+        // A dummy callback is required to send route feature info.
         RouteCallback routeCallback = new RouteCallback();
         MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
             @Override
@@ -394,16 +394,17 @@
             }
 
             @Override
-            public void onControlCategoriesChanged(String packageName, List<String> routeTypes) {
+            public void onControlCategoriesChanged(String packageName,
+                    List<String> preferredFeatures) {
                 if (TextUtils.equals(mPackageName, packageName)
-                        && routeTypes.equals(routeTypes)) {
+                        && preferredFeatures.equals(preferredFeatures)) {
                     latch.countDown();
                 }
             }
         };
         mManager.registerCallback(mExecutor, managerCallback);
         mRouter2.registerRouteCallback(mExecutor, routeCallback,
-                new RouteDiscoveryRequest.Builder(routeTypes, true).build());
+                new RouteDiscoveryPreference.Builder(routeFeatures, true).build());
         try {
             latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
             return createRouteMap(mManager.getAvailableRoutes(mPackageName));
@@ -450,7 +451,7 @@
 
     private void addRouterCallback(RouteCallback routeCallback) {
         mRouteCallbacks.add(routeCallback);
-        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY);
+        mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.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/RouteDiscoveryPreferenceTest.java
similarity index 75%
rename from media/tests/MediaRouter/src/com/android/mediaroutertest/RouteDiscoveryRequestTest.java
rename to media/tests/MediaRouter/src/com/android/mediaroutertest/RouteDiscoveryPreferenceTest.java
index 60d131a..fa12935 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteDiscoveryRequestTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteDiscoveryPreferenceTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
-import android.media.RouteDiscoveryRequest;
+import android.media.RouteDiscoveryPreference;
 import android.os.Parcel;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -34,7 +34,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-public class RouteDiscoveryRequestTest {
+public class RouteDiscoveryPreferenceTest {
     @Before
     public void setUp() throws Exception { }
 
@@ -46,10 +46,10 @@
         List<String> testTypes = new ArrayList<>();
         testTypes.add("TEST_TYPE_1");
         testTypes.add("TEST_TYPE_2");
-        RouteDiscoveryRequest request = new RouteDiscoveryRequest.Builder(testTypes, true)
+        RouteDiscoveryPreference request = new RouteDiscoveryPreference.Builder(testTypes, true)
                 .build();
 
-        RouteDiscoveryRequest requestRebuilt = new RouteDiscoveryRequest.Builder(request)
+        RouteDiscoveryPreference requestRebuilt = new RouteDiscoveryPreference.Builder(request)
                 .build();
 
         assertEquals(request, requestRebuilt);
@@ -57,7 +57,7 @@
         Parcel parcel = Parcel.obtain();
         parcel.writeParcelable(request, 0);
         parcel.setDataPosition(0);
-        RouteDiscoveryRequest requestFromParcel = parcel.readParcelable(null);
+        RouteDiscoveryPreference requestFromParcel = parcel.readParcelable(null);
 
         assertEquals(request, requestFromParcel);
     }
@@ -71,15 +71,15 @@
         List<String> testTypes2 = new ArrayList<>();
         testTypes.add("TEST_TYPE_3");
 
-        RouteDiscoveryRequest request = new RouteDiscoveryRequest.Builder(testTypes, true)
+        RouteDiscoveryPreference request = new RouteDiscoveryPreference.Builder(testTypes, true)
                 .build();
 
-        RouteDiscoveryRequest requestTypes = new RouteDiscoveryRequest.Builder(request)
-                .setRouteTypes(testTypes2)
+        RouteDiscoveryPreference requestTypes = new RouteDiscoveryPreference.Builder(request)
+                .setPreferredFeatures(testTypes2)
                 .build();
         assertNotEquals(request, requestTypes);
 
-        RouteDiscoveryRequest requestActiveScan = new RouteDiscoveryRequest.Builder(request)
+        RouteDiscoveryPreference requestActiveScan = new RouteDiscoveryPreference.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
deleted file mode 100644
index 9971fc3..0000000
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mediaroutertest;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.media.RouteSessionInfo;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@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";
-
-    private static final String TEST_ROUTE_ID1 = "route_id1";
-
-    @Test
-    public void testValidity() {
-        RouteSessionInfo emptyPackageSession = new RouteSessionInfo.Builder(TEST_SESSION_ID,
-                "",
-                TEST_CONTROL_CATEGORY)
-                .addSelectedRoute(TEST_ROUTE_ID1)
-                .build();
-        RouteSessionInfo emptyCategorySession = new RouteSessionInfo.Builder(TEST_SESSION_ID,
-                TEST_PACKAGE_NAME, "")
-                .addSelectedRoute(TEST_ROUTE_ID1)
-                .build();
-
-        RouteSessionInfo emptySelectedRouteSession = new RouteSessionInfo.Builder(TEST_SESSION_ID,
-                TEST_PACKAGE_NAME, TEST_CONTROL_CATEGORY)
-                .build();
-
-        RouteSessionInfo validSession = new RouteSessionInfo.Builder(emptySelectedRouteSession)
-                .addSelectedRoute(TEST_ROUTE_ID1)
-                .build();
-
-        assertFalse(emptyPackageSession.isValid());
-        assertFalse(emptyCategorySession.isValid());
-        assertFalse(emptySelectedRouteSession.isValid());
-        assertTrue(validSession.isValid());
-    }
-}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
new file mode 100644
index 0000000..3f59736
--- /dev/null
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
@@ -0,0 +1,560 @@
+/*
+ * 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 static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertThrows;
+
+import android.media.RoutingSessionInfo;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests {@link RoutingSessionInfo} and its {@link RoutingSessionInfo.Builder builder}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RoutingSessionInfoTest {
+    public static final String TEST_ID = "test_id";
+    public static final String TEST_CLIENT_PACKAGE_NAME = "com.test.client.package.name";
+    public static final String TEST_ROUTE_FEATURE = "test_route_feature";
+
+    public static final String TEST_ROUTE_ID_0 = "test_route_type_0";
+    public static final String TEST_ROUTE_ID_1 = "test_route_type_1";
+    public static final String TEST_ROUTE_ID_2 = "test_route_type_2";
+    public static final String TEST_ROUTE_ID_3 = "test_route_type_3";
+    public static final String TEST_ROUTE_ID_4 = "test_route_type_4";
+    public static final String TEST_ROUTE_ID_5 = "test_route_type_5";
+    public static final String TEST_ROUTE_ID_6 = "test_route_type_6";
+    public static final String TEST_ROUTE_ID_7 = "test_route_type_7";
+
+    public static final String TEST_KEY = "test_key";
+    public static final String TEST_VALUE = "test_value";
+
+    @Test
+    public void testBuilderConstructorWithInvalidValues() {
+        final String nullId = null;
+        final String nullClientPackageName = null;
+        final String nullRouteFeature = null;
+
+        final String emptyId = "";
+        // Note: An empty string as client package name is valid.
+        final String emptyRouteFeature = "";
+
+        final String validId = TEST_ID;
+        final String validClientPackageName = TEST_CLIENT_PACKAGE_NAME;
+        final String validRouteFeature = TEST_ROUTE_FEATURE;
+
+        // ID is invalid
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                nullId, validClientPackageName, validRouteFeature));
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                emptyId, validClientPackageName, validRouteFeature));
+
+        // client package name is invalid (null)
+        assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder(
+                validId, nullClientPackageName, validRouteFeature));
+
+        // route feature is invalid
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                validId, validClientPackageName, nullRouteFeature));
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                validId, validClientPackageName, emptyRouteFeature));
+
+        // Two arguments are invalid - (1) ID and clientPackageName
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                nullId, nullClientPackageName, validRouteFeature));
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                emptyId, nullClientPackageName, validRouteFeature));
+
+        // Two arguments are invalid - (2) ID and routeFeature
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                nullId, validClientPackageName, nullRouteFeature));
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                nullId, validClientPackageName, emptyRouteFeature));
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                emptyId, validClientPackageName, nullRouteFeature));
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                emptyId, validClientPackageName, emptyRouteFeature));
+
+        // Two arguments are invalid - (3) clientPackageName and routeFeature
+        // Note that this throws NullPointerException.
+        assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder(
+                validId, nullClientPackageName, nullRouteFeature));
+        assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder(
+                validId, nullClientPackageName, emptyRouteFeature));
+
+        // All arguments are invalid
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                nullId, nullClientPackageName, nullRouteFeature));
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                nullId, nullClientPackageName, emptyRouteFeature));
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                emptyId, nullClientPackageName, nullRouteFeature));
+        assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
+                emptyId, nullClientPackageName, emptyRouteFeature));
+
+        // Null RouteInfo (1-argument constructor)
+        final RoutingSessionInfo nullRoutingSessionInfo = null;
+        assertThrows(NullPointerException.class,
+                () -> new RoutingSessionInfo.Builder(nullRoutingSessionInfo));
+    }
+
+    @Test
+    public void testBuilderConstructorWithEmptyClientPackageName() {
+        // An empty string for client package name is valid. (for unknown cases)
+        // Creating builder with it should not throw any exception.
+        RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
+                TEST_ID, "" /* clientPackageName*/, TEST_ROUTE_FEATURE);
+    }
+
+    @Test
+    public void testBuilderBuildWithEmptySelectedRoutesThrowsIAE() {
+        RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE);
+        // Note: Calling build() without adding any selected routes.
+        assertThrows(IllegalArgumentException.class, () -> builder.build());
+    }
+
+    @Test
+    public void testBuilderAddRouteMethodsWithIllegalArgumentsThrowsIAE() {
+        RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE);
+
+        final String nullRouteId = null;
+        final String emptyRouteId = "";
+
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.addSelectedRoute(nullRouteId));
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.addSelectableRoute(nullRouteId));
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.addDeselectableRoute(nullRouteId));
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.addTransferrableRoute(nullRouteId));
+
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.addSelectedRoute(emptyRouteId));
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.addSelectableRoute(emptyRouteId));
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.addDeselectableRoute(emptyRouteId));
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.addTransferrableRoute(emptyRouteId));
+    }
+
+    @Test
+    public void testBuilderRemoveRouteMethodsWithIllegalArgumentsThrowsIAE() {
+        RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE);
+
+        final String nullRouteId = null;
+        final String emptyRouteId = "";
+
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.removeSelectedRoute(nullRouteId));
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.removeSelectableRoute(nullRouteId));
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.removeDeselectableRoute(nullRouteId));
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.removeTransferrableRoute(nullRouteId));
+
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.removeSelectedRoute(emptyRouteId));
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.removeSelectableRoute(emptyRouteId));
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.removeDeselectableRoute(emptyRouteId));
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.removeTransferrableRoute(emptyRouteId));
+    }
+
+    @Test
+    public void testBuilderAndGettersOfRoutingSessionInfo() {
+        Bundle controlHints = new Bundle();
+        controlHints.putString(TEST_KEY, TEST_VALUE);
+
+        RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .addSelectedRoute(TEST_ROUTE_ID_1)
+                .addSelectableRoute(TEST_ROUTE_ID_2)
+                .addSelectableRoute(TEST_ROUTE_ID_3)
+                .addDeselectableRoute(TEST_ROUTE_ID_4)
+                .addDeselectableRoute(TEST_ROUTE_ID_5)
+                .addTransferrableRoute(TEST_ROUTE_ID_6)
+                .addTransferrableRoute(TEST_ROUTE_ID_7)
+                .setControlHints(controlHints)
+                .build();
+
+        assertEquals(TEST_ID, sessionInfo.getId());
+        assertEquals(TEST_CLIENT_PACKAGE_NAME, sessionInfo.getClientPackageName());
+        assertEquals(TEST_ROUTE_FEATURE, sessionInfo.getRouteFeature());
+
+        assertEquals(2, sessionInfo.getSelectedRoutes().size());
+        assertEquals(TEST_ROUTE_ID_0, sessionInfo.getSelectedRoutes().get(0));
+        assertEquals(TEST_ROUTE_ID_1, sessionInfo.getSelectedRoutes().get(1));
+
+        assertEquals(2, sessionInfo.getSelectableRoutes().size());
+        assertEquals(TEST_ROUTE_ID_2, sessionInfo.getSelectableRoutes().get(0));
+        assertEquals(TEST_ROUTE_ID_3, sessionInfo.getSelectableRoutes().get(1));
+
+        assertEquals(2, sessionInfo.getDeselectableRoutes().size());
+        assertEquals(TEST_ROUTE_ID_4, sessionInfo.getDeselectableRoutes().get(0));
+        assertEquals(TEST_ROUTE_ID_5, sessionInfo.getDeselectableRoutes().get(1));
+
+        assertEquals(2, sessionInfo.getTransferrableRoutes().size());
+        assertEquals(TEST_ROUTE_ID_6, sessionInfo.getTransferrableRoutes().get(0));
+        assertEquals(TEST_ROUTE_ID_7, sessionInfo.getTransferrableRoutes().get(1));
+
+        Bundle controlHintsOut = sessionInfo.getControlHints();
+        assertNotNull(controlHintsOut);
+        assertTrue(controlHintsOut.containsKey(TEST_KEY));
+        assertEquals(TEST_VALUE, controlHintsOut.getString(TEST_KEY));
+    }
+
+    @Test
+    public void testBuilderAddRouteMethodsWithBuilderCopyConstructor() {
+        RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .addSelectableRoute(TEST_ROUTE_ID_2)
+                .addDeselectableRoute(TEST_ROUTE_ID_4)
+                .addTransferrableRoute(TEST_ROUTE_ID_6)
+                .build();
+
+        RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
+                .addSelectedRoute(TEST_ROUTE_ID_1)
+                .addSelectableRoute(TEST_ROUTE_ID_3)
+                .addDeselectableRoute(TEST_ROUTE_ID_5)
+                .addTransferrableRoute(TEST_ROUTE_ID_7)
+                .build();
+
+        assertEquals(2, newSessionInfo.getSelectedRoutes().size());
+        assertEquals(TEST_ROUTE_ID_0, newSessionInfo.getSelectedRoutes().get(0));
+        assertEquals(TEST_ROUTE_ID_1, newSessionInfo.getSelectedRoutes().get(1));
+
+        assertEquals(2, newSessionInfo.getSelectableRoutes().size());
+        assertEquals(TEST_ROUTE_ID_2, newSessionInfo.getSelectableRoutes().get(0));
+        assertEquals(TEST_ROUTE_ID_3, newSessionInfo.getSelectableRoutes().get(1));
+
+        assertEquals(2, newSessionInfo.getDeselectableRoutes().size());
+        assertEquals(TEST_ROUTE_ID_4, newSessionInfo.getDeselectableRoutes().get(0));
+        assertEquals(TEST_ROUTE_ID_5, newSessionInfo.getDeselectableRoutes().get(1));
+
+        assertEquals(2, newSessionInfo.getTransferrableRoutes().size());
+        assertEquals(TEST_ROUTE_ID_6, newSessionInfo.getTransferrableRoutes().get(0));
+        assertEquals(TEST_ROUTE_ID_7, newSessionInfo.getTransferrableRoutes().get(1));
+    }
+
+    @Test
+    public void testBuilderRemoveRouteMethods() {
+        RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .addSelectedRoute(TEST_ROUTE_ID_1)
+                .removeSelectedRoute(TEST_ROUTE_ID_1)
+
+                .addSelectableRoute(TEST_ROUTE_ID_2)
+                .addSelectableRoute(TEST_ROUTE_ID_3)
+                .removeSelectableRoute(TEST_ROUTE_ID_3)
+
+                .addDeselectableRoute(TEST_ROUTE_ID_4)
+                .addDeselectableRoute(TEST_ROUTE_ID_5)
+                .removeDeselectableRoute(TEST_ROUTE_ID_5)
+
+                .addTransferrableRoute(TEST_ROUTE_ID_6)
+                .addTransferrableRoute(TEST_ROUTE_ID_7)
+                .removeTransferrableRoute(TEST_ROUTE_ID_7)
+
+                .build();
+
+        assertEquals(1, sessionInfo.getSelectedRoutes().size());
+        assertEquals(TEST_ROUTE_ID_0, sessionInfo.getSelectedRoutes().get(0));
+
+        assertEquals(1, sessionInfo.getSelectableRoutes().size());
+        assertEquals(TEST_ROUTE_ID_2, sessionInfo.getSelectableRoutes().get(0));
+
+        assertEquals(1, sessionInfo.getDeselectableRoutes().size());
+        assertEquals(TEST_ROUTE_ID_4, sessionInfo.getDeselectableRoutes().get(0));
+
+        assertEquals(1, sessionInfo.getTransferrableRoutes().size());
+        assertEquals(TEST_ROUTE_ID_6, sessionInfo.getTransferrableRoutes().get(0));
+    }
+
+    @Test
+    public void testBuilderRemoveRouteMethodsWithBuilderCopyConstructor() {
+        RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .addSelectedRoute(TEST_ROUTE_ID_1)
+                .addSelectableRoute(TEST_ROUTE_ID_2)
+                .addSelectableRoute(TEST_ROUTE_ID_3)
+                .addDeselectableRoute(TEST_ROUTE_ID_4)
+                .addDeselectableRoute(TEST_ROUTE_ID_5)
+                .addTransferrableRoute(TEST_ROUTE_ID_6)
+                .addTransferrableRoute(TEST_ROUTE_ID_7)
+                .build();
+
+        RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
+                .removeSelectedRoute(TEST_ROUTE_ID_1)
+                .removeSelectableRoute(TEST_ROUTE_ID_3)
+                .removeDeselectableRoute(TEST_ROUTE_ID_5)
+                .removeTransferrableRoute(TEST_ROUTE_ID_7)
+                .build();
+
+        assertEquals(1, newSessionInfo.getSelectedRoutes().size());
+        assertEquals(TEST_ROUTE_ID_0, newSessionInfo.getSelectedRoutes().get(0));
+
+        assertEquals(1, newSessionInfo.getSelectableRoutes().size());
+        assertEquals(TEST_ROUTE_ID_2, newSessionInfo.getSelectableRoutes().get(0));
+
+        assertEquals(1, newSessionInfo.getDeselectableRoutes().size());
+        assertEquals(TEST_ROUTE_ID_4, newSessionInfo.getDeselectableRoutes().get(0));
+
+        assertEquals(1, newSessionInfo.getTransferrableRoutes().size());
+        assertEquals(TEST_ROUTE_ID_6, newSessionInfo.getTransferrableRoutes().get(0));
+    }
+
+    @Test
+    public void testBuilderClearRouteMethods() {
+        RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .addSelectedRoute(TEST_ROUTE_ID_1)
+                .clearSelectedRoutes()
+
+                .addSelectableRoute(TEST_ROUTE_ID_2)
+                .addSelectableRoute(TEST_ROUTE_ID_3)
+                .clearSelectableRoutes()
+
+                .addDeselectableRoute(TEST_ROUTE_ID_4)
+                .addDeselectableRoute(TEST_ROUTE_ID_5)
+                .clearDeselectableRoutes()
+
+                .addTransferrableRoute(TEST_ROUTE_ID_6)
+                .addTransferrableRoute(TEST_ROUTE_ID_7)
+                .clearTransferrableRoutes()
+
+                // SelectedRoutes must not be empty
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .build();
+
+        assertEquals(1, sessionInfo.getSelectedRoutes().size());
+        assertEquals(TEST_ROUTE_ID_0, sessionInfo.getSelectedRoutes().get(0));
+
+        assertTrue(sessionInfo.getSelectableRoutes().isEmpty());
+        assertTrue(sessionInfo.getDeselectableRoutes().isEmpty());
+        assertTrue(sessionInfo.getTransferrableRoutes().isEmpty());
+    }
+
+    @Test
+    public void testBuilderClearRouteMethodsWithBuilderCopyConstructor() {
+        RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .addSelectedRoute(TEST_ROUTE_ID_1)
+                .addSelectableRoute(TEST_ROUTE_ID_2)
+                .addSelectableRoute(TEST_ROUTE_ID_3)
+                .addDeselectableRoute(TEST_ROUTE_ID_4)
+                .addDeselectableRoute(TEST_ROUTE_ID_5)
+                .addTransferrableRoute(TEST_ROUTE_ID_6)
+                .addTransferrableRoute(TEST_ROUTE_ID_7)
+                .build();
+
+        RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
+                .clearSelectedRoutes()
+                .clearSelectableRoutes()
+                .clearDeselectableRoutes()
+                .clearTransferrableRoutes()
+                // SelectedRoutes must not be empty
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .build();
+
+        assertEquals(1, newSessionInfo.getSelectedRoutes().size());
+        assertEquals(TEST_ROUTE_ID_0, newSessionInfo.getSelectedRoutes().get(0));
+
+        assertTrue(newSessionInfo.getSelectableRoutes().isEmpty());
+        assertTrue(newSessionInfo.getDeselectableRoutes().isEmpty());
+        assertTrue(newSessionInfo.getTransferrableRoutes().isEmpty());
+    }
+
+    @Test
+    public void testEqualsCreatedWithSameArguments() {
+        Bundle controlHints = new Bundle();
+        controlHints.putString(TEST_KEY, TEST_VALUE);
+
+        RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .addSelectedRoute(TEST_ROUTE_ID_1)
+                .addSelectableRoute(TEST_ROUTE_ID_2)
+                .addSelectableRoute(TEST_ROUTE_ID_3)
+                .addDeselectableRoute(TEST_ROUTE_ID_4)
+                .addDeselectableRoute(TEST_ROUTE_ID_5)
+                .addTransferrableRoute(TEST_ROUTE_ID_6)
+                .addTransferrableRoute(TEST_ROUTE_ID_7)
+                .setControlHints(controlHints)
+                .build();
+
+        RoutingSessionInfo sessionInfo2 = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .addSelectedRoute(TEST_ROUTE_ID_1)
+                .addSelectableRoute(TEST_ROUTE_ID_2)
+                .addSelectableRoute(TEST_ROUTE_ID_3)
+                .addDeselectableRoute(TEST_ROUTE_ID_4)
+                .addDeselectableRoute(TEST_ROUTE_ID_5)
+                .addTransferrableRoute(TEST_ROUTE_ID_6)
+                .addTransferrableRoute(TEST_ROUTE_ID_7)
+                .setControlHints(controlHints)
+                .build();
+
+        assertEquals(sessionInfo1, sessionInfo2);
+        assertEquals(sessionInfo1.hashCode(), sessionInfo2.hashCode());
+    }
+
+    @Test
+    public void testEqualsCreatedWithBuilderCopyConstructor() {
+        Bundle controlHints = new Bundle();
+        controlHints.putString(TEST_KEY, TEST_VALUE);
+
+        RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .addSelectedRoute(TEST_ROUTE_ID_1)
+                .addSelectableRoute(TEST_ROUTE_ID_2)
+                .addSelectableRoute(TEST_ROUTE_ID_3)
+                .addDeselectableRoute(TEST_ROUTE_ID_4)
+                .addDeselectableRoute(TEST_ROUTE_ID_5)
+                .addTransferrableRoute(TEST_ROUTE_ID_6)
+                .addTransferrableRoute(TEST_ROUTE_ID_7)
+                .setControlHints(controlHints)
+                .build();
+
+        RoutingSessionInfo sessionInfo2 = new RoutingSessionInfo.Builder(sessionInfo1).build();
+
+        assertEquals(sessionInfo1, sessionInfo2);
+        assertEquals(sessionInfo1.hashCode(), sessionInfo2.hashCode());
+    }
+
+    @Test
+    public void testEqualsReturnFalse() {
+        Bundle controlHints = new Bundle();
+        controlHints.putString(TEST_KEY, TEST_VALUE);
+
+        RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .addSelectedRoute(TEST_ROUTE_ID_1)
+                .addSelectableRoute(TEST_ROUTE_ID_2)
+                .addSelectableRoute(TEST_ROUTE_ID_3)
+                .addDeselectableRoute(TEST_ROUTE_ID_4)
+                .addDeselectableRoute(TEST_ROUTE_ID_5)
+                .addTransferrableRoute(TEST_ROUTE_ID_6)
+                .addTransferrableRoute(TEST_ROUTE_ID_7)
+                .setControlHints(controlHints)
+                .build();
+
+        // Now, we will use copy constructor
+        assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
+                .addSelectedRoute("randomRoute")
+                .build());
+        assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
+                .addSelectableRoute("randomRoute")
+                .build());
+        assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
+                .addDeselectableRoute("randomRoute")
+                .build());
+        assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
+                .addTransferrableRoute("randomRoute")
+                .build());
+
+        assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
+                .removeSelectedRoute(TEST_ROUTE_ID_1)
+                .build());
+        assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
+                .removeSelectableRoute(TEST_ROUTE_ID_3)
+                .build());
+        assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
+                .removeDeselectableRoute(TEST_ROUTE_ID_5)
+                .build());
+        assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
+                .removeTransferrableRoute(TEST_ROUTE_ID_7)
+                .build());
+
+        assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
+                .clearSelectedRoutes()
+                // Note: Calling build() with empty selected routes will throw IAE.
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .build());
+        assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
+                .clearSelectableRoutes()
+                .build());
+        assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
+                .clearDeselectableRoutes()
+                .build());
+        assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
+                .clearTransferrableRoutes()
+                .build());
+
+        // Note: ControlHints will not affect the equals.
+    }
+
+    @Test
+    public void testParcelingAndUnParceling() {
+        Bundle controlHints = new Bundle();
+        controlHints.putString(TEST_KEY, TEST_VALUE);
+
+        RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
+                TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+                .addSelectedRoute(TEST_ROUTE_ID_0)
+                .addSelectedRoute(TEST_ROUTE_ID_1)
+                .addSelectableRoute(TEST_ROUTE_ID_2)
+                .addSelectableRoute(TEST_ROUTE_ID_3)
+                .addDeselectableRoute(TEST_ROUTE_ID_4)
+                .addDeselectableRoute(TEST_ROUTE_ID_5)
+                .addTransferrableRoute(TEST_ROUTE_ID_6)
+                .addTransferrableRoute(TEST_ROUTE_ID_7)
+                .setControlHints(controlHints)
+                .build();
+
+        Parcel parcel = Parcel.obtain();
+        sessionInfo.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        RoutingSessionInfo sessionInfoFromParcel =
+                RoutingSessionInfo.CREATOR.createFromParcel(parcel);
+        assertEquals(sessionInfo, sessionInfoFromParcel);
+        assertEquals(sessionInfo.hashCode(), sessionInfoFromParcel.hashCode());
+
+        // Check controlHints
+        Bundle controlHintsOut = sessionInfoFromParcel.getControlHints();
+        assertNotNull(controlHintsOut);
+        assertTrue(controlHintsOut.containsKey(TEST_KEY));
+        assertEquals(TEST_VALUE, controlHintsOut.getString(TEST_KEY));
+    }
+}
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index cb04d92..c1f8b3f 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -46,6 +46,7 @@
 # <extension1> and <extension2> are mapped (or remapped) to <mimeType>.
 
 ?application/epub+zip epub
+?application/lrc lrc
 ?application/pkix-cert cer
 ?application/rss+xml rss
 ?application/sdp sdp
@@ -65,6 +66,7 @@
 ?application/x-mpegurl m3u m3u8
 ?application/x-pem-file pem
 ?application/x-pkcs12 p12 pfx
+?application/x-subrip srt
 ?application/x-webarchive webarchive
 ?application/x-webarchive-xml webarchivexml
 ?application/x-x509-server-cert crt
diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java
index 6554267..cf55eba 100644
--- a/mms/java/android/telephony/MmsManager.java
+++ b/mms/java/android/telephony/MmsManager.java
@@ -16,8 +16,12 @@
 
 package android.telephony;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
 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 +31,18 @@
 
 /**
  * Manages MMS operations such as sending multimedia messages.
- * Get this object by calling the static method {@link #getInstance()}.
- * @hide
+ * Get this object by calling Context#getSystemService(Context#MMS_SERVICE).
  */
+@SystemService(Context.MMS_SERVICE)
 public 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 +56,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 +85,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/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 942eafd..376ea77 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -24,12 +24,21 @@
 
     // our source files
     //
-    srcs: ["bitmap.cpp"],
+    srcs: [
+        "aassetstreamadaptor.cpp",
+        "bitmap.cpp",
+        "imagedecoder.cpp",
+    ],
 
     shared_libs: [
+        "libandroid",
         "libandroid_runtime",
+        "libhwui",
+        "liblog",
     ],
 
+    static_libs: ["libarect"],
+
     arch: {
         arm: {
             // TODO: This is to work around b/24465209. Remove after root cause is fixed
diff --git a/native/graphics/jni/aassetstreamadaptor.cpp b/native/graphics/jni/aassetstreamadaptor.cpp
new file mode 100644
index 0000000..016008b
--- /dev/null
+++ b/native/graphics/jni/aassetstreamadaptor.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#include "aassetstreamadaptor.h"
+
+#include <log/log.h>
+
+AAssetStreamAdaptor::AAssetStreamAdaptor(AAsset* asset)
+    : mAAsset(asset)
+{
+}
+
+bool AAssetStreamAdaptor::rewind() {
+    off64_t pos = AAsset_seek64(mAAsset, 0, SEEK_SET);
+    if (pos == (off64_t)-1) {
+        ALOGE("rewind failed!");
+        return false;
+    }
+    return true;
+}
+
+size_t AAssetStreamAdaptor::getLength() const {
+    return AAsset_getLength64(mAAsset);
+}
+
+bool AAssetStreamAdaptor::isAtEnd() const {
+    return AAsset_getRemainingLength64(mAAsset) == 0;
+}
+
+SkStreamRewindable* AAssetStreamAdaptor::onDuplicate() const {
+    // Cannot sensibly create a duplicate, since each AAssetStreamAdaptor
+    // would be modifying the same AAsset.
+    //return new AAssetStreamAdaptor(mAAsset);
+    return nullptr;
+}
+
+bool AAssetStreamAdaptor::hasPosition() const {
+    return AAsset_seek64(mAAsset, 0, SEEK_CUR) != -1;
+}
+
+size_t AAssetStreamAdaptor::getPosition() const {
+    const off64_t offset = AAsset_seek64(mAAsset, 0, SEEK_CUR);
+    if (offset == -1) {
+        ALOGE("getPosition failed!");
+        return 0;
+    }
+
+    return offset;
+}
+
+bool AAssetStreamAdaptor::seek(size_t position) {
+    if (AAsset_seek64(mAAsset, position, SEEK_SET) == -1) {
+        ALOGE("seek failed!");
+        return false;
+    }
+
+    return true;
+}
+
+bool AAssetStreamAdaptor::move(long offset) {
+    if (AAsset_seek64(mAAsset, offset, SEEK_CUR) == -1) {
+        ALOGE("move failed!");
+        return false;
+    }
+
+    return true;
+}
+
+size_t AAssetStreamAdaptor::read(void* buffer, size_t size) {
+    ssize_t amount;
+
+    if (!buffer) {
+        if (!size) {
+            return 0;
+        }
+
+        // asset->seek returns new total offset
+        // we want to return amount that was skipped
+        const off64_t oldOffset = AAsset_seek64(mAAsset, 0, SEEK_CUR);
+        if (oldOffset == -1) {
+            ALOGE("seek(oldOffset) failed!");
+            return 0;
+        }
+
+        const off64_t newOffset = AAsset_seek64(mAAsset, size, SEEK_CUR);
+        if (-1 == newOffset) {
+            ALOGE("seek(%zu) failed!", size);
+            return 0;
+        }
+        amount = newOffset - oldOffset;
+    } else {
+        amount = AAsset_read(mAAsset, buffer, size);
+    }
+
+    if (amount < 0) {
+        amount = 0;
+    }
+    return amount;
+}
+
+const void* AAssetStreamAdaptor::getMemoryBase() {
+    return AAsset_getBuffer(mAAsset);
+}
+
diff --git a/native/graphics/jni/aassetstreamadaptor.h b/native/graphics/jni/aassetstreamadaptor.h
new file mode 100644
index 0000000..c1fddb0
--- /dev/null
+++ b/native/graphics/jni/aassetstreamadaptor.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "SkStream.h"
+
+#include <android/asset_manager.h>
+
+// Like AssetStreamAdaptor, but operates on AAsset, a public NDK API.
+class AAssetStreamAdaptor : public SkStreamRewindable {
+public:
+    /**
+     * Create an SkStream that reads from an AAsset.
+     *
+     * The AAsset must remain open for the lifetime of the Adaptor. The Adaptor
+     * does *not* close the AAsset.
+     */
+    explicit AAssetStreamAdaptor(AAsset*);
+
+    bool rewind() override;
+    size_t read(void* buffer, size_t size) override;
+    bool hasLength() const override { return true; }
+    size_t getLength() const override;
+    bool hasPosition() const override;
+    size_t getPosition() const override;
+    bool seek(size_t position) override;
+    bool move(long offset) override;
+    bool isAtEnd() const override;
+    const void* getMemoryBase() override;
+protected:
+    SkStreamRewindable* onDuplicate() const override;
+
+private:
+    AAsset* mAAsset;
+};
+
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
new file mode 100644
index 0000000..2ef203d
--- /dev/null
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -0,0 +1,320 @@
+/*
+ * 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.
+ */
+
+#include "aassetstreamadaptor.h"
+
+#include <android/asset_manager.h>
+#include <android/bitmap.h>
+#include <android/imagedecoder.h>
+#include <android/graphics/MimeType.h>
+#include <android/rect.h>
+#include <hwui/ImageDecoder.h>
+#include <log/log.h>
+#include <SkAndroidCodec.h>
+
+#include <fcntl.h>
+#include <optional>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+using namespace android;
+
+int ResultToErrorCode(SkCodec::Result result) {
+    switch (result) {
+        case SkCodec::kIncompleteInput:
+            return ANDROID_IMAGE_DECODER_INCOMPLETE;
+        case SkCodec::kErrorInInput:
+            return ANDROID_IMAGE_DECODER_ERROR;
+        case SkCodec::kInvalidInput:
+            return ANDROID_IMAGE_DECODER_INVALID_INPUT;
+        case SkCodec::kCouldNotRewind:
+            return ANDROID_IMAGE_DECODER_SEEK_ERROR;
+        case SkCodec::kUnimplemented:
+            return ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT;
+        case SkCodec::kInvalidConversion:
+            return ANDROID_IMAGE_DECODER_INVALID_CONVERSION;
+        case SkCodec::kInvalidParameters:
+            return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+        case SkCodec::kSuccess:
+            return ANDROID_IMAGE_DECODER_SUCCESS;
+        case SkCodec::kInvalidScale:
+            return ANDROID_IMAGE_DECODER_INVALID_SCALE;
+        case SkCodec::kInternalError:
+            return ANDROID_IMAGE_DECODER_INTERNAL_ERROR;
+    }
+}
+
+static int createFromStream(std::unique_ptr<SkStreamRewindable> stream, AImageDecoder** outDecoder) {
+    SkCodec::Result result;
+    auto codec = SkCodec::MakeFromStream(std::move(stream), &result, nullptr,
+                                         SkCodec::SelectionPolicy::kPreferAnimation);
+    auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
+            SkAndroidCodec::ExifOrientationBehavior::kRespect);
+    if (!androidCodec) {
+        return ResultToErrorCode(result);
+    }
+
+    *outDecoder = reinterpret_cast<AImageDecoder*>(new ImageDecoder(std::move(androidCodec)));
+    return ANDROID_IMAGE_DECODER_SUCCESS;
+}
+
+int AImageDecoder_createFromAAsset(AAsset* asset, AImageDecoder** outDecoder) {
+    if (!asset || !outDecoder) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+    *outDecoder = nullptr;
+
+    auto stream = std::make_unique<AAssetStreamAdaptor>(asset);
+    return createFromStream(std::move(stream), outDecoder);
+}
+
+static bool isSeekable(int descriptor) {
+    return ::lseek64(descriptor, 0, SEEK_CUR) != -1;
+}
+
+int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) {
+    if (fd <= 0 || !outDecoder) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+
+    struct stat fdStat;
+    if (fstat(fd, &fdStat) == -1) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+
+    if (!isSeekable(fd)) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+
+    // SkFILEStream will close its descriptor. Duplicate it so the client will
+    // still be responsible for closing the original.
+    int dupDescriptor = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+    FILE* file = fdopen(dupDescriptor, "r");
+    if (!file) {
+        close(dupDescriptor);
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+
+    auto stream = std::unique_ptr<SkStreamRewindable>(new SkFILEStream(file));
+    return createFromStream(std::move(stream), outDecoder);
+}
+
+int AImageDecoder_createFromBuffer(const void* buffer, size_t length,
+                                   AImageDecoder** outDecoder) {
+    if (!buffer || !length  || !outDecoder) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+    *outDecoder = nullptr;
+
+    // The client is expected to keep the buffer alive as long as the
+    // AImageDecoder, so we do not need to copy the buffer.
+    auto stream = std::unique_ptr<SkStreamRewindable>(
+            new SkMemoryStream(buffer, length, false /* copyData */));
+    return createFromStream(std::move(stream), outDecoder);
+}
+
+static ImageDecoder* toDecoder(AImageDecoder* d) {
+    return reinterpret_cast<ImageDecoder*>(d);
+}
+
+// Note: This differs from the version in android_bitmap.cpp in that this
+// version returns kGray_8_SkColorType for ANDROID_BITMAP_FORMAT_A_8. SkCodec
+// allows decoding single channel images to gray, which Android then treats
+// as A_8/ALPHA_8.
+static SkColorType getColorType(AndroidBitmapFormat format) {
+    switch (format) {
+        case ANDROID_BITMAP_FORMAT_RGBA_8888:
+            return kN32_SkColorType;
+        case ANDROID_BITMAP_FORMAT_RGB_565:
+            return kRGB_565_SkColorType;
+        case ANDROID_BITMAP_FORMAT_RGBA_4444:
+            return kARGB_4444_SkColorType;
+        case ANDROID_BITMAP_FORMAT_A_8:
+            return kGray_8_SkColorType;
+        case ANDROID_BITMAP_FORMAT_RGBA_F16:
+            return kRGBA_F16_SkColorType;
+        default:
+            return kUnknown_SkColorType;
+    }
+}
+
+int AImageDecoder_setAndroidBitmapFormat(AImageDecoder* decoder, int32_t format) {
+    if (!decoder || format < ANDROID_BITMAP_FORMAT_NONE
+            || format > ANDROID_BITMAP_FORMAT_RGBA_F16) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+    return toDecoder(decoder)->setOutColorType(getColorType((AndroidBitmapFormat) format))
+            ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION;
+}
+
+const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(const AImageDecoder* decoder) {
+    return reinterpret_cast<const AImageDecoderHeaderInfo*>(decoder);
+}
+
+static const ImageDecoder* toDecoder(const AImageDecoderHeaderInfo* info) {
+    return reinterpret_cast<const ImageDecoder*>(info);
+}
+
+int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo* info) {
+    if (!info) {
+        return 0;
+    }
+    return toDecoder(info)->mCodec->getInfo().width();
+}
+
+int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo* info) {
+    if (!info) {
+        return 0;
+    }
+    return toDecoder(info)->mCodec->getInfo().height();
+}
+
+const char* AImageDecoderHeaderInfo_getMimeType(const AImageDecoderHeaderInfo* info) {
+    if (!info) {
+        return nullptr;
+    }
+    return getMimeType(toDecoder(info)->mCodec->getEncodedFormat());
+}
+
+bool AImageDecoderHeaderInfo_isAnimated(const AImageDecoderHeaderInfo* info) {
+    if (!info) {
+        return false;
+    }
+    return toDecoder(info)->mCodec->codec()->getFrameCount() > 1;
+}
+
+// FIXME: Share with getFormat in android_bitmap.cpp?
+static AndroidBitmapFormat getFormat(SkColorType colorType) {
+    switch (colorType) {
+        case kN32_SkColorType:
+            return ANDROID_BITMAP_FORMAT_RGBA_8888;
+        case kRGB_565_SkColorType:
+            return ANDROID_BITMAP_FORMAT_RGB_565;
+        case kARGB_4444_SkColorType:
+            return ANDROID_BITMAP_FORMAT_RGBA_4444;
+        case kAlpha_8_SkColorType:
+            return ANDROID_BITMAP_FORMAT_A_8;
+        case kRGBA_F16_SkColorType:
+            return ANDROID_BITMAP_FORMAT_RGBA_F16;
+        default:
+            return ANDROID_BITMAP_FORMAT_NONE;
+    }
+}
+
+AndroidBitmapFormat AImageDecoderHeaderInfo_getAndroidBitmapFormat(
+        const AImageDecoderHeaderInfo* info) {
+    if (!info) {
+        return ANDROID_BITMAP_FORMAT_NONE;
+    }
+    return getFormat(toDecoder(info)->mCodec->computeOutputColorType(kN32_SkColorType));
+}
+
+int AImageDecoderHeaderInfo_getAlphaFlags(const AImageDecoderHeaderInfo* info) {
+    if (!info) {
+        // FIXME: Better invalid?
+        return -1;
+    }
+    switch (toDecoder(info)->mCodec->getInfo().alphaType()) {
+        case kUnknown_SkAlphaType:
+            LOG_ALWAYS_FATAL("Invalid alpha type");
+            return -1;
+        case kUnpremul_SkAlphaType:
+            // fall through. premul is the default.
+        case kPremul_SkAlphaType:
+            return ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
+        case kOpaque_SkAlphaType:
+            return ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE;
+    }
+}
+
+SkAlphaType toAlphaType(int androidBitmapFlags) {
+    switch (androidBitmapFlags) {
+        case ANDROID_BITMAP_FLAGS_ALPHA_PREMUL:
+            return kPremul_SkAlphaType;
+        case ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL:
+            return kUnpremul_SkAlphaType;
+        case ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE:
+            return kOpaque_SkAlphaType;
+        default:
+            return kUnknown_SkAlphaType;
+    }
+}
+
+int AImageDecoder_setAlphaFlags(AImageDecoder* decoder, int alphaFlag) {
+    if (!decoder || alphaFlag < ANDROID_BITMAP_FLAGS_ALPHA_PREMUL
+            || alphaFlag > ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+
+    return toDecoder(decoder)->setOutAlphaType(toAlphaType(alphaFlag))
+            ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION;
+}
+
+int AImageDecoder_setTargetSize(AImageDecoder* decoder, int width, int height) {
+    if (!decoder) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+
+    return toDecoder(decoder)->setTargetSize(width, height)
+            ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_SCALE;
+}
+
+int AImageDecoder_setCrop(AImageDecoder* decoder, ARect crop) {
+    if (!decoder) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+
+    SkIRect cropIRect;
+    cropIRect.setLTRB(crop.left, crop.top, crop.right, crop.bottom);
+    SkIRect* cropPtr = cropIRect == SkIRect::MakeEmpty() ? nullptr : &cropIRect;
+    return toDecoder(decoder)->setCropRect(cropPtr)
+            ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+}
+
+
+size_t AImageDecoder_getMinimumStride(AImageDecoder* decoder) {
+    if (!decoder) {
+        return 0;
+    }
+
+    SkImageInfo info = toDecoder(decoder)->getOutputInfo();
+    return info.minRowBytes();
+}
+
+int AImageDecoder_decodeImage(AImageDecoder* decoder,
+                              void* pixels, size_t stride,
+                              size_t size) {
+    if (!decoder || !pixels || !stride) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+
+    ImageDecoder* imageDecoder = toDecoder(decoder);
+
+    const int height = imageDecoder->getOutputInfo().height();
+    const size_t minStride = AImageDecoder_getMinimumStride(decoder);
+    // If this calculation were to overflow, it would have been caught in
+    // setTargetSize.
+    if (stride < minStride || size < stride * (height - 1) + minStride) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+
+    return ResultToErrorCode(imageDecoder->decode(pixels, stride));
+}
+
+void AImageDecoder_delete(AImageDecoder* decoder) {
+    delete toDecoder(decoder);
+}
diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt
index a601d8a..bdd7f63 100644
--- a/native/graphics/jni/libjnigraphics.map.txt
+++ b/native/graphics/jni/libjnigraphics.map.txt
@@ -1,5 +1,22 @@
 LIBJNIGRAPHICS {
   global:
+    AImageDecoder_createFromAAsset;
+    AImageDecoder_createFromFd;
+    AImageDecoder_createFromBuffer;
+    AImageDecoder_delete;
+    AImageDecoder_setAndroidBitmapFormat;
+    AImageDecoder_setAlphaFlags;
+    AImageDecoder_getHeaderInfo;
+    AImageDecoder_getMinimumStride;
+    AImageDecoder_decodeImage;
+    AImageDecoder_setTargetSize;
+    AImageDecoder_setCrop;
+    AImageDecoderHeaderInfo_getWidth;
+    AImageDecoderHeaderInfo_getHeight;
+    AImageDecoderHeaderInfo_getMimeType;
+    AImageDecoderHeaderInfo_getAlphaFlags;
+    AImageDecoderHeaderInfo_isAnimated;
+    AImageDecoderHeaderInfo_getAndroidBitmapFormat;
     AndroidBitmap_getInfo;
     AndroidBitmap_lockPixels;
     AndroidBitmap_unlockPixels;
diff --git a/core/res/res/interpolator/screen_rotation_alpha_in.xml b/packages/CarSystemUI/res/layout/sysui_primary_window.xml
similarity index 68%
copy from core/res/res/interpolator/screen_rotation_alpha_in.xml
copy to packages/CarSystemUI/res/layout/sysui_primary_window.xml
index 9c566a7..309d9ef 100644
--- a/core/res/res/interpolator/screen_rotation_alpha_in.xml
+++ b/packages/CarSystemUI/res/layout/sysui_primary_window.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 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.
@@ -15,8 +15,9 @@
   ~ 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"/>
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:background="@android:color/transparent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java
new file mode 100644
index 0000000..e3e9ab7
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java
@@ -0,0 +1,137 @@
+/*
+ * 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.car;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.os.Binder;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Controls the expansion state of the primary window which will contain all of the fullscreen sysui
+ * behavior. This window still has a collapsed state in order to watch for swipe events to expand
+ * this window for the notification panel.
+ */
+@Singleton
+public class SystemUIPrimaryWindowController implements
+        ConfigurationController.ConfigurationListener {
+
+    private final Context mContext;
+    private final Resources mResources;
+    private final WindowManager mWindowManager;
+
+    private final int mStatusBarHeight;
+    private final int mNavBarHeight;
+    private final int mDisplayHeight;
+    private ViewGroup mBaseLayout;
+    private WindowManager.LayoutParams mLp;
+    private WindowManager.LayoutParams mLpChanged;
+
+    @Inject
+    public SystemUIPrimaryWindowController(
+            Context context,
+            @Main Resources resources,
+            WindowManager windowManager,
+            ConfigurationController configurationController
+    ) {
+        mContext = context;
+        mResources = resources;
+        mWindowManager = windowManager;
+
+        Point display = new Point();
+        mWindowManager.getDefaultDisplay().getSize(display);
+        mDisplayHeight = display.y;
+
+        mStatusBarHeight = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height);
+        mNavBarHeight = mResources.getDimensionPixelSize(R.dimen.navigation_bar_height);
+
+        mLpChanged = new WindowManager.LayoutParams();
+        mBaseLayout = (ViewGroup) LayoutInflater.from(context)
+                .inflate(R.layout.sysui_primary_window, /* root= */ null, false);
+
+        configurationController.addCallback(this);
+    }
+
+    /** Returns the base view of the primary window. */
+    public ViewGroup getBaseLayout() {
+        return mBaseLayout;
+    }
+
+    /** Attaches the window to the window manager. */
+    public void attach() {
+        // Now that the status bar window encompasses the sliding panel and its
+        // translucent backdrop, the entire thing is made TRANSLUCENT and is
+        // hardware-accelerated.
+        mLp = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                mStatusBarHeight,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+                PixelFormat.TRANSLUCENT);
+        mLp.token = new Binder();
+        mLp.gravity = Gravity.TOP;
+        mLp.setFitWindowInsetsTypes(/* types= */ 0);
+        mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+        mLp.setTitle("NotificationShade");
+        mLp.packageName = mContext.getPackageName();
+        mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+        mWindowManager.addView(mBaseLayout, mLp);
+        mLpChanged.copyFrom(mLp);
+    }
+
+    /** Sets the window to the expanded state. */
+    public void setWindowExpanded(boolean expanded) {
+        if (expanded) {
+            // TODO: Update this so that the windowing type gets the full height of the display
+            //  when we use MATCH_PARENT.
+            mLpChanged.height = mDisplayHeight + mNavBarHeight;
+        } else {
+            mLpChanged.height = mStatusBarHeight;
+        }
+        updateWindow();
+    }
+
+    /** Returns {@code true} if the window is expanded */
+    public boolean isWindowExpanded() {
+        return mLp.height != mStatusBarHeight;
+    }
+
+    private void updateWindow() {
+        if (mLp != null && mLp.copyFrom(mLpChanged) != 0) {
+            mWindowManager.updateViewLayout(mBaseLayout, mLp);
+        }
+    }
+}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 642dc82..41a9b24 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -36,6 +36,7 @@
 import android.os.Bundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -49,7 +50,6 @@
 import android.widget.TextView;
 
 import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.TrafficStatsConstants;
 
@@ -203,7 +203,7 @@
     }
 
     private URL getUrlForCaptivePortal() {
-        String url = getIntent().getStringExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY);
+        String url = getIntent().getStringExtra(TelephonyManager.EXTRA_REDIRECTION_URL);
         if (TextUtils.isEmpty(url)) url = mCm.getCaptivePortalServerUrl();
         final CarrierConfigManager configManager = getApplicationContext()
                 .getSystemService(CarrierConfigManager.class);
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
index 46b1d5f..c7f5e9a 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
@@ -19,10 +19,10 @@
 import android.content.Intent;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
@@ -50,7 +50,7 @@
      * @param intent passing signal for config match
      * @return a list of carrier action for the given signal based on the carrier config.
      *
-     *  Example: input intent TelephonyIntent.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+     *  Example: input intent TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
      *  This intent allows fined-grained matching based on both intent type & extra values:
      *  apnType and errorCode.
      *  apnType read from passing intent is "default" and errorCode is 0x26 for example and
@@ -78,25 +78,25 @@
             String arg1 = null;
             String arg2 = null;
             switch (intent.getAction()) {
-                case TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED:
+                case TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED:
                     configs = b.getStringArray(CarrierConfigManager
                             .KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY);
                     break;
-                case TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED:
+                case TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED:
                     configs = b.getStringArray(CarrierConfigManager
                             .KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY);
-                    arg1 = intent.getStringExtra(TelephonyIntents.EXTRA_APN_TYPE_KEY);
-                    arg2 = intent.getStringExtra(TelephonyIntents.EXTRA_ERROR_CODE_KEY);
+                    arg1 = intent.getStringExtra(TelephonyManager.EXTRA_APN_TYPE);
+                    arg2 = intent.getStringExtra(TelephonyManager.EXTRA_ERROR_CODE);
                     break;
-                case TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET:
+                case TelephonyManager.ACTION_CARRIER_SIGNAL_RESET:
                     configs = b.getStringArray(CarrierConfigManager
                             .KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET);
                     break;
-                case TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE:
+                case TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE:
                     configs = b.getStringArray(CarrierConfigManager
                             .KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE);
-                    arg1 = String.valueOf(intent.getBooleanExtra(TelephonyIntents
-                            .EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY, false));
+                    arg1 = String.valueOf(intent.getBooleanExtra(TelephonyManager
+                            .EXTRA_DEFAULT_NETWORK_AVAILABLE, false));
                     break;
                 default:
                     Log.e(TAG, "load carrier config failure with un-configured key: "
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
index 3e34f0a..78a02d7 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
@@ -27,10 +27,9 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 
-import com.android.internal.telephony.TelephonyIntents;
-
 /**
  * Service to run {@link android.app.job.JobScheduler} job.
  * Service to monitor when there is a change to conent URI
@@ -93,7 +92,7 @@
         }
         int jobId;
         switch(intent.getAction()) {
-            case TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED:
+            case TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED:
                 jobId = PROVISION_OBSERVER_REEVALUATION_JOB_ID;
                 break;
             default:
diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
index 1928ad9..086a287 100644
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
+++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
@@ -15,6 +15,11 @@
  */
 package com.android.carrierdefaultapp;
 
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -25,7 +30,6 @@
 import android.test.InstrumentationTestCase;
 
 import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyIntents;
 
 import org.junit.After;
 import org.junit.Before;
@@ -34,10 +38,6 @@
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 
 public class CarrierDefaultReceiverTest extends InstrumentationTestCase {
     @Mock
@@ -87,7 +87,7 @@
                 .KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY, new String[]{"4,1"});
         doReturn(b).when(mCarrierConfigMgr).getConfig();
 
-        Intent intent = new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED);
+        Intent intent = new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED);
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
         mReceiver.onReceive(mContext, intent);
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
index 69bd0ed..ff00fb3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
@@ -16,8 +16,6 @@
 
 package com.android.settingslib;
 
-import static android.content.Context.TELEPHONY_SERVICE;
-
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -172,36 +170,38 @@
         }
     }
 
-    public static String getFormattedPhoneNumber(Context context, SubscriptionInfo subscriptionInfo) {
+    /**
+     * Format a phone number.
+     * @param subscriptionInfo {@link SubscriptionInfo} subscription information.
+     * @return Returns formatted phone number.
+     */
+    public static String getFormattedPhoneNumber(Context context,
+            SubscriptionInfo subscriptionInfo) {
         String formattedNumber = null;
         if (subscriptionInfo != null) {
-            final TelephonyManager telephonyManager =
-                    (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
-            final String rawNumber =
-                    telephonyManager.getLine1Number(subscriptionInfo.getSubscriptionId());
+            final TelephonyManager telephonyManager = context.getSystemService(
+                    TelephonyManager.class);
+            final String rawNumber = telephonyManager.createForSubscriptionId(
+                    subscriptionInfo.getSubscriptionId()).getLine1Number();
             if (!TextUtils.isEmpty(rawNumber)) {
                 formattedNumber = PhoneNumberUtils.formatNumber(rawNumber);
             }
-
         }
         return formattedNumber;
     }
 
     public static String getFormattedPhoneNumbers(Context context,
-            List<SubscriptionInfo> subscriptionInfo) {
+            List<SubscriptionInfo> subscriptionInfoList) {
         StringBuilder sb = new StringBuilder();
-        if (subscriptionInfo != null) {
-            final TelephonyManager telephonyManager =
-                    (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
-            final int count = subscriptionInfo.size();
-            for (int i = 0; i < count; i++) {
-                final String rawNumber = telephonyManager.getLine1Number(
-                        subscriptionInfo.get(i).getSubscriptionId());
+        if (subscriptionInfoList != null) {
+            final TelephonyManager telephonyManager = context.getSystemService(
+                    TelephonyManager.class);
+            final int count = subscriptionInfoList.size();
+            for (SubscriptionInfo subscriptionInfo : subscriptionInfoList) {
+                final String rawNumber = telephonyManager.createForSubscriptionId(
+                        subscriptionInfo.getSubscriptionId()).getLine1Number();
                 if (!TextUtils.isEmpty(rawNumber)) {
-                    sb.append(PhoneNumberUtils.formatNumber(rawNumber));
-                    if (i < count - 1) {
-                        sb.append("\n");
-                    }
+                    sb.append(PhoneNumberUtils.formatNumber(rawNumber)).append("\n");
                 }
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
index ebca962..853c77e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
@@ -38,7 +38,7 @@
         final SubscriptionManager subscriptionManager = context.getSystemService(
                 SubscriptionManager.class);
         final NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
-                telephonyManager.getSubscriberId(subId));
+                telephonyManager.createForSubscriptionId(subId).getSubscriberId());
 
         if (!subscriptionManager.isActiveSubId(subId)) {
             Log.i(TAG, "Subscription is not active: " + subId);
diff --git a/packages/SystemUI/res/layout-land/global_actions_grid_item.xml b/packages/SystemUI/res/layout-land/global_actions_grid_item.xml
index bc12338..0f9deaa 100644
--- a/packages/SystemUI/res/layout-land/global_actions_grid_item.xml
+++ b/packages/SystemUI/res/layout-land/global_actions_grid_item.xml
@@ -57,15 +57,5 @@
             android:textColor="@color/global_actions_text"
             android:textAppearance="?android:attr/textAppearanceSmall"
         />
-
-        <TextView
-            android:visibility="gone"
-            android:id="@*android:id/status"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:gravity="center"
-            android:textColor="@color/global_actions_text"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-        />
     </LinearLayout>
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_item.xml b/packages/SystemUI/res/layout/global_actions_grid_item.xml
index 4404c874..31c7cbf 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_item.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml
@@ -56,15 +56,5 @@
             android:textColor="@color/global_actions_text"
             android:textAppearance="?android:attr/textAppearanceSmall"
         />
-
-        <TextView
-            android:visibility="gone"
-            android:id="@*android:id/status"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:gravity="center"
-            android:textColor="@color/global_actions_text"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-        />
     </LinearLayout>
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 6ac9da4..995cb7d 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
diff --git a/packages/SystemUI/res/layout/global_screenshot_legacy.xml b/packages/SystemUI/res/layout/global_screenshot_legacy.xml
new file mode 100644
index 0000000..791c7ea
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_screenshot_legacy.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <ImageView android:id="@+id/global_screenshot_legacy_background"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:src="@android:color/black"
+        android:visibility="gone" />
+    <ImageView android:id="@+id/global_screenshot_legacy"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:background="@drawable/screenshot_panel"
+        android:visibility="gone"
+        android:adjustViewBounds="true" />
+    <ImageView android:id="@+id/global_screenshot_legacy_flash"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:src="@android:color/white"
+        android:visibility="gone" />
+    <com.android.systemui.screenshot.ScreenshotSelectorView
+        android:id="@+id/global_screenshot_legacy_selector"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone"
+        android:pointerIcon="crosshair"/>
+</FrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index da0323a..5a1151e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -288,6 +288,7 @@
     <!-- Dimensions related to screenshots -->
 
     <!-- The padding on the global screenshot background image -->
+    <dimen name="global_screenshot_legacy_bg_padding">20dp</dimen>
     <dimen name="global_screenshot_bg_padding">20dp</dimen>
     <dimen name="screenshot_action_container_corner_radius">10dp</dimen>
     <dimen name="screenshot_action_container_padding">20dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index b083123..23d6458 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -18,7 +18,6 @@
 
 import android.app.AppOpsManager;
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
@@ -64,7 +63,6 @@
     private H mBGHandler;
     private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
     private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>();
-    private final PermissionFlagsCache mFlagsCache;
     private boolean mListening;
 
     @GuardedBy("mActiveItems")
@@ -81,17 +79,10 @@
     };
 
     @Inject
-    public AppOpsControllerImpl(Context context, @Background Looper bgLooper,
-            DumpController dumpController) {
-        this(context, bgLooper, new PermissionFlagsCache(context), dumpController);
-    }
-
-    @VisibleForTesting
-    protected AppOpsControllerImpl(Context context, Looper bgLooper, PermissionFlagsCache cache,
-            DumpController dumpController) {
+    public AppOpsControllerImpl(Context context,
+            @Background Looper bgLooper, DumpController dumpController) {
         mContext = context;
         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-        mFlagsCache = cache;
         mBGHandler = new H(bgLooper);
         final int numOps = OPS.length;
         for (int i = 0; i < numOps; i++) {
@@ -209,7 +200,14 @@
             mNotedItems.remove(item);
             if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
         }
-        notifySuscribers(code, uid, packageName, false);
+        boolean active;
+        // Check if the item is also active
+        synchronized (mActiveItems) {
+            active = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null;
+        }
+        if (!active) {
+            notifySuscribers(code, uid, packageName, false);
+        }
     }
 
     private boolean addNoted(int code, int uid, String packageName) {
@@ -224,64 +222,13 @@
                 createdNew = true;
             }
         }
+        // We should keep this so we make sure it cannot time out.
+        mBGHandler.removeCallbacksAndMessages(item);
         mBGHandler.scheduleRemoval(item, NOTED_OP_TIME_DELAY_MS);
         return createdNew;
     }
 
     /**
-     * Does the app-op code refer to a user sensitive permission for the specified user id
-     * and package. Only user sensitive permission should be shown to the user by default.
-     *
-     * @param appOpCode The code of the app-op.
-     * @param uid The uid of the user.
-     * @param packageName The name of the package.
-     *
-     * @return {@code true} iff the app-op item is user sensitive
-     */
-    private boolean isUserSensitive(int appOpCode, int uid, String packageName) {
-        String permission = AppOpsManager.opToPermission(appOpCode);
-        if (permission == null) {
-            return false;
-        }
-        int permFlags = mFlagsCache.getPermissionFlags(permission,
-                packageName, UserHandle.getUserHandleForUid(uid));
-        return (permFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
-    }
-
-    /**
-     * Does the app-op item refer to an operation that should be shown to the user.
-     * Only specficic ops (like SYSTEM_ALERT_WINDOW) or ops that refer to user sensitive
-     * permission should be shown to the user by default.
-     *
-     * @param item The item
-     *
-     * @return {@code true} iff the app-op item should be shown to the user
-     */
-    private boolean isUserVisible(AppOpItem item) {
-        return isUserVisible(item.getCode(), item.getUid(), item.getPackageName());
-    }
-
-
-    /**
-     * Does the app-op, uid and package name, refer to an operation that should be shown to the
-     * user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or
-     * ops that refer to user sensitive permission should be shown to the user by default.
-     *
-     * @param item The item
-     *
-     * @return {@code true} iff the app-op for should be shown to the user
-     */
-    private boolean isUserVisible(int appOpCode, int uid, String packageName) {
-        // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission
-        // which may be user senstive, so for now always show it to the user.
-        if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) {
-            return true;
-        }
-
-        return isUserSensitive(appOpCode, uid, packageName);
-    }
-
-    /**
      * Returns a copy of the list containing all the active AppOps that the controller tracks.
      *
      * @return List of active AppOps information
@@ -304,8 +251,8 @@
             final int numActiveItems = mActiveItems.size();
             for (int i = 0; i < numActiveItems; i++) {
                 AppOpItem item = mActiveItems.get(i);
-                if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId)
-                        && isUserVisible(item)) {
+                if ((userId == UserHandle.USER_ALL
+                        || UserHandle.getUserId(item.getUid()) == userId)) {
                     list.add(item);
                 }
             }
@@ -314,8 +261,8 @@
             final int numNotedItems = mNotedItems.size();
             for (int i = 0; i < numNotedItems; i++) {
                 AppOpItem item = mNotedItems.get(i);
-                if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId)
-                        && isUserVisible(item)) {
+                if ((userId == UserHandle.USER_ALL
+                        || UserHandle.getUserId(item.getUid()) == userId)) {
                     list.add(item);
                 }
             }
@@ -325,7 +272,21 @@
 
     @Override
     public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
-        if (updateActives(code, uid, packageName, active)) {
+        if (DEBUG) {
+            Log.w(TAG, String.format("onActiveChanged(%d,%d,%s,%s", code, uid, packageName,
+                    Boolean.toString(active)));
+        }
+        boolean activeChanged = updateActives(code, uid, packageName, active);
+        if (!activeChanged) return; // early return
+        // Check if the item is also noted, in that case, there's no update.
+        boolean alsoNoted;
+        synchronized (mNotedItems) {
+            alsoNoted = getAppOpItemLocked(mNotedItems, code, uid, packageName) != null;
+        }
+        // If active is true, we only send the update if the op is not actively noted (already true)
+        // If active is false, we only send the update if the op is not actively noted (prevent
+        // early removal)
+        if (!alsoNoted) {
             mBGHandler.post(() -> notifySuscribers(code, uid, packageName, active));
         }
     }
@@ -333,17 +294,23 @@
     @Override
     public void onOpNoted(int code, int uid, String packageName, int result) {
         if (DEBUG) {
-            Log.w(TAG, "Op: " + code + " with result " + AppOpsManager.MODE_NAMES[result]);
+            Log.w(TAG, "Noted op: " + code + " with result "
+                    + AppOpsManager.MODE_NAMES[result] + " for package " + packageName);
         }
         if (result != AppOpsManager.MODE_ALLOWED) return;
-        if (addNoted(code, uid, packageName)) {
+        boolean notedAdded = addNoted(code, uid, packageName);
+        if (!notedAdded) return; // early return
+        boolean alsoActive;
+        synchronized (mActiveItems) {
+            alsoActive = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null;
+        }
+        if (!alsoActive) {
             mBGHandler.post(() -> notifySuscribers(code, uid, packageName, true));
         }
     }
 
     private void notifySuscribers(int code, int uid, String packageName, boolean active) {
-        if (mCallbacksByCode.containsKey(code)
-                && isUserVisible(code, uid, packageName)) {
+        if (mCallbacksByCode.containsKey(code)) {
             if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
             for (Callback cb: mCallbacksByCode.get(code)) {
                 cb.onActiveStateChanged(code, uid, packageName, active);
@@ -368,7 +335,7 @@
 
     }
 
-    protected final class H extends Handler {
+    protected class H extends Handler {
         H(Looper looper) {
             super(looper);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
deleted file mode 100644
index f02c7af..0000000
--- a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.appops
-
-import android.content.Context
-import android.content.pm.PackageManager
-import android.os.UserHandle
-import android.util.ArrayMap
-import com.android.internal.annotations.VisibleForTesting
-
-private data class PermissionFlag(val flag: Int, val timestamp: Long)
-
-private data class PermissionFlagKey(
-    val permission: String,
-    val packageName: String,
-    val user: UserHandle
-)
-
-internal const val CACHE_EXPIRATION = 10000L
-
-/**
- * Cache for PackageManager's PermissionFlags.
- *
- * Flags older than [CACHE_EXPIRATION] will be retrieved again.
- */
-internal open class PermissionFlagsCache(context: Context) {
-    private val packageManager = context.packageManager
-    private val permissionFlagsCache = ArrayMap<PermissionFlagKey, PermissionFlag>()
-
-    /**
-     * Retrieve permission flags from cache or PackageManager. There parameters will be passed
-     * directly to [PackageManager].
-     *
-     * Calls to this method should be done from a background thread.
-     */
-    fun getPermissionFlags(permission: String, packageName: String, user: UserHandle): Int {
-        val key = PermissionFlagKey(permission, packageName, user)
-        val now = getCurrentTime()
-        val value = permissionFlagsCache.getOrPut(key) {
-            PermissionFlag(getFlags(key), now)
-        }
-        if (now - value.timestamp > CACHE_EXPIRATION) {
-            val newValue = PermissionFlag(getFlags(key), now)
-            permissionFlagsCache.put(key, newValue)
-            return newValue.flag
-        } else {
-            return value.flag
-        }
-    }
-
-    private fun getFlags(key: PermissionFlagKey) =
-            packageManager.getPermissionFlags(key.permission, key.packageName, key.user)
-
-    @VisibleForTesting
-    protected open fun getCurrentTime() = System.currentTimeMillis()
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index fc19fa0..91bb80c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1166,13 +1166,6 @@
             TextView messageView = (TextView) v.findViewById(R.id.message);
             messageView.setSelected(true); // necessary for marquee to work
 
-            TextView statusView = (TextView) v.findViewById(R.id.status);
-            final String status = getStatus();
-            if (!TextUtils.isEmpty(status)) {
-                statusView.setText(status);
-            } else {
-                statusView.setVisibility(View.GONE);
-            }
             if (mIcon != null) {
                 icon.setImageDrawable(mIcon);
                 icon.setScaleType(ScaleType.CENTER_CROP);
@@ -1257,32 +1250,26 @@
                 LayoutInflater inflater) {
             willCreate();
 
-            View v = inflater.inflate(R
-                    .layout.global_actions_item, parent, false);
+            View v = inflater.inflate(com.android.systemui.R
+                    .layout.global_actions_grid_item, parent, false);
 
             ImageView icon = (ImageView) v.findViewById(R.id.icon);
             TextView messageView = (TextView) v.findViewById(R.id.message);
-            TextView statusView = (TextView) v.findViewById(R.id.status);
             final boolean enabled = isEnabled();
+            boolean on = ((mState == State.On) || (mState == State.TurningOn));
 
             if (messageView != null) {
-                messageView.setText(mMessageResId);
+                messageView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
                 messageView.setEnabled(enabled);
                 messageView.setSelected(true); // necessary for marquee to work
             }
 
-            boolean on = ((mState == State.On) || (mState == State.TurningOn));
             if (icon != null) {
                 icon.setImageDrawable(context.getDrawable(
                         (on ? mEnabledIconResId : mDisabledIconResid)));
                 icon.setEnabled(enabled);
             }
 
-            if (statusView != null) {
-                statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
-                statusView.setVisibility(View.VISIBLE);
-                statusView.setEnabled(enabled);
-            }
             v.setEnabled(enabled);
 
             return v;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 02c4beb..9f2bbc6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -20,7 +20,7 @@
 import static android.view.View.VISIBLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_CORNER_FLOW;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_SCROLLING_ENABLED;
 import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
 
 import android.animation.Animator;
@@ -246,20 +246,13 @@
             mSaveInBgTask.cancel(false);
         }
 
-        if (!DeviceConfig.getBoolean(
-                NAMESPACE_SYSTEMUI, SCREENSHOT_CORNER_FLOW, false)) {
-            mNotificationsController.reset();
-            mNotificationsController.setImage(mScreenBitmap);
-            mNotificationsController.showSavingScreenshotNotification();
-        }
         mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data).execute();
     }
 
     /**
      * Takes a screenshot of the current display and shows an animation.
      */
-    private void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible,
-            boolean navBarVisible, Rect crop) {
+    private void takeScreenshot(Consumer<Uri> finisher, Rect crop) {
         int rot = mDisplay.getRotation();
         int width = crop.width();
         int height = crop.height();
@@ -278,21 +271,20 @@
         mScreenBitmap.prepareToDraw();
 
         // Start the post-screenshot animation
-        startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
-                statusBarVisible, navBarVisible);
+        startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels);
     }
 
-    void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, boolean navBarVisible) {
+    void takeScreenshot(Consumer<Uri> finisher) {
         mDisplay.getRealMetrics(mDisplayMetrics);
-        takeScreenshot(finisher, statusBarVisible, navBarVisible,
+        takeScreenshot(
+                finisher,
                 new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
     }
 
     /**
      * Displays a screenshot selector
      */
-    void takeScreenshotPartial(final Consumer<Uri> finisher, final boolean statusBarVisible,
-            final boolean navBarVisible) {
+    void takeScreenshotPartial(final Consumer<Uri> finisher) {
         mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
         mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
             @Override
@@ -312,8 +304,7 @@
                         if (rect != null) {
                             if (rect.width() != 0 && rect.height() != 0) {
                                 // Need mScreenshotLayout to handle it after the view disappears
-                                mScreenshotLayout.post(() -> takeScreenshot(
-                                        finisher, statusBarVisible, navBarVisible, rect));
+                                mScreenshotLayout.post(() -> takeScreenshot(finisher, rect));
                             }
                         }
 
@@ -364,8 +355,7 @@
     /**
      * Starts the animation after taking the screenshot
      */
-    private void startAnimation(final Consumer<Uri> finisher, int w, int h,
-            boolean statusBarVisible, boolean navBarVisible) {
+    private void startAnimation(final Consumer<Uri> finisher, int w, int h) {
         // If power save is on, show a toast so there is some visual indication that a screenshot
         // has been taken.
         PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -385,50 +375,30 @@
             mScreenshotAnimation.removeAllListeners();
         }
 
-        boolean useCornerFlow =
-                DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, SCREENSHOT_CORNER_FLOW, false);
         mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
         ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
-        ValueAnimator screenshotFadeOutAnim = useCornerFlow
-                ? createScreenshotToCornerAnimation(w, h)
-                : createScreenshotDropOutAnimation(w, h, statusBarVisible, navBarVisible);
+        ValueAnimator screenshotFadeOutAnim = createScreenshotToCornerAnimation(w, h);
         mScreenshotAnimation = new AnimatorSet();
         mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
         mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 // Save the screenshot once we have a bit of time now
-                if (!useCornerFlow) {
-                    saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
-                        @Override
-                        void onActionsReady(Uri uri, List<Notification.Action> actions) {
-                            if (uri == null) {
-                                mNotificationsController.notifyScreenshotError(
-                                        R.string.screenshot_failed_to_capture_text);
-                            } else {
-                                mNotificationsController
-                                        .showScreenshotActionsNotification(uri, actions);
-                            }
+                saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
+                    @Override
+                    void onActionsReady(Uri uri, List<Notification.Action> actions) {
+                        if (uri == null) {
+                            mNotificationsController.notifyScreenshotError(
+                                    R.string.screenshot_failed_to_capture_text);
+                        } else {
+                            mScreenshotHandler.post(() ->
+                                    createScreenshotActionsShadeAnimation(actions).start());
                         }
-                    });
-                    clearScreenshot();
-                } else {
-                    saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
-                        @Override
-                        void onActionsReady(Uri uri, List<Notification.Action> actions) {
-                            if (uri == null) {
-                                mNotificationsController.notifyScreenshotError(
-                                        R.string.screenshot_failed_to_capture_text);
-                            } else {
-                                mScreenshotHandler.post(() ->
-                                        createScreenshotActionsShadeAnimation(actions).start());
-                            }
-                        }
-                    });
-                    mScreenshotHandler.sendMessageDelayed(
-                            mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
-                            SCREENSHOT_CORNER_TIMEOUT_MILLIS);
-                }
+                    }
+                });
+                mScreenshotHandler.sendMessageDelayed(
+                        mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
+                        SCREENSHOT_CORNER_TIMEOUT_MILLIS);
             }
         });
         mScreenshotHandler.post(() -> {
@@ -492,7 +462,7 @@
             }
 
             @Override
-            public void onAnimationEnd(android.animation.Animator animation) {
+            public void onAnimationEnd(Animator animation) {
                 mScreenshotFlash.setVisibility(View.GONE);
             }
         });
@@ -513,81 +483,6 @@
         return anim;
     }
 
-    private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,
-            boolean navBarVisible) {
-        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
-        anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
-        anim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mBackgroundView.setVisibility(View.GONE);
-                mScreenshotView.setVisibility(View.GONE);
-                mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
-            }
-        });
-
-        if (!statusBarVisible || !navBarVisible) {
-            // There is no status bar/nav bar, so just fade the screenshot away in place
-            anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION);
-            anim.addUpdateListener(new AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    float t = (Float) animation.getAnimatedValue();
-                    float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
-                            - t * (SCREENSHOT_DROP_IN_MIN_SCALE
-                            - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);
-                    mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
-                    mScreenshotView.setAlpha(1f - t);
-                    mScreenshotView.setScaleX(scaleT);
-                    mScreenshotView.setScaleY(scaleT);
-                }
-            });
-        } else {
-            // In the case where there is a status bar, animate to the origin of the bar (top-left)
-            final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION
-                    / SCREENSHOT_DROP_OUT_DURATION;
-            final Interpolator scaleInterpolator = new Interpolator() {
-                @Override
-                public float getInterpolation(float x) {
-                    if (x < scaleDurationPct) {
-                        // Decelerate, and scale the input accordingly
-                        return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f));
-                    }
-                    return 1f;
-                }
-            };
-
-            // Determine the bounds of how to scale
-            float halfScreenWidth = (w - 2f * mBgPadding) / 2f;
-            float halfScreenHeight = (h - 2f * mBgPadding) / 2f;
-            final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET;
-            final PointF finalPos = new PointF(
-                    -halfScreenWidth
-                            + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
-                    -halfScreenHeight
-                            + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);
-
-            // Animate the screenshot to the status bar
-            anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
-            anim.addUpdateListener(new AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    float t = (Float) animation.getAnimatedValue();
-                    float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
-                            - scaleInterpolator.getInterpolation(t)
-                            * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE);
-                    mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
-                    mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t));
-                    mScreenshotView.setScaleX(scaleT);
-                    mScreenshotView.setScaleY(scaleT);
-                    mScreenshotView.setTranslationX(t * finalPos.x);
-                    mScreenshotView.setTranslationY(t * finalPos.y);
-                }
-            });
-        }
-        return anim;
-    }
-
     private ValueAnimator createScreenshotToCornerAnimation(int w, int h) {
         ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
         anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
@@ -648,13 +543,16 @@
             });
             mActionsView.addView(actionChip);
         }
-        TextView scrollChip = (TextView) inflater.inflate(
-                R.layout.global_screenshot_action_chip, mActionsView, false);
-        Toast scrollNotImplemented = Toast.makeText(
-                mContext, "Not implemented", Toast.LENGTH_SHORT);
-        scrollChip.setText("Scroll"); // TODO (mkephart): add resource and translate
-        scrollChip.setOnClickListener(v -> scrollNotImplemented.show());
-        mActionsView.addView(scrollChip);
+
+        if (DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, SCREENSHOT_SCROLLING_ENABLED, false)) {
+            TextView scrollChip = (TextView) inflater.inflate(
+                    R.layout.global_screenshot_action_chip, mActionsView, false);
+            Toast scrollNotImplemented = Toast.makeText(
+                    mContext, "Not implemented", Toast.LENGTH_SHORT);
+            scrollChip.setText("Scroll"); // TODO (mkephart): add resource and translate
+            scrollChip.setOnClickListener(v -> scrollNotImplemented.show());
+            mActionsView.addView(scrollChip);
+        }
 
         ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
         mActionsView.setY(mDisplayMetrics.heightPixels);
@@ -776,8 +674,7 @@
             String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
             Slog.d(TAG, "Executing smart action [" + actionType + "]:" + actionIntent);
             ActivityOptions opts = ActivityOptions.makeBasic();
-            context.startActivityAsUser(actionIntent, opts.toBundle(),
-                    UserHandle.CURRENT);
+            context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT);
 
             ScreenshotSmartActions.notifyScreenshotAction(
                     context, intent.getStringExtra(EXTRA_ID), actionType, true);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
new file mode 100644
index 0000000..11aa80b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2011 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.screenshot;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.media.MediaActionSound;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.PowerManager;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Class for handling device screen shots
+ *
+ * @deprecated will be removed when corner flow is complete and tested
+ */
+@Singleton
+@Deprecated
+public class GlobalScreenshotLegacy {
+
+    // These strings are used for communicating the action invoked to
+    // ScreenshotNotificationSmartActionsProvider.
+    static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
+    static final String EXTRA_ID = "android:screenshot_id";
+    static final String ACTION_TYPE_DELETE = "Delete";
+    static final String ACTION_TYPE_SHARE = "Share";
+    static final String ACTION_TYPE_EDIT = "Edit";
+    static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
+    static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
+
+    static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
+    static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
+    static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
+
+    private static final String TAG = "GlobalScreenshot";
+
+    private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
+    private static final int SCREENSHOT_DROP_IN_DURATION = 430;
+    private static final int SCREENSHOT_DROP_OUT_DELAY = 500;
+    private static final int SCREENSHOT_DROP_OUT_DURATION = 430;
+    private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370;
+    private static final int SCREENSHOT_FAST_DROP_OUT_DURATION = 320;
+    private static final float BACKGROUND_ALPHA = 0.5f;
+    private static final float SCREENSHOT_SCALE = 1f;
+    private static final float SCREENSHOT_DROP_IN_MIN_SCALE = SCREENSHOT_SCALE * 0.725f;
+    private static final float SCREENSHOT_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.45f;
+    private static final float SCREENSHOT_FAST_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.6f;
+    private static final float SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET = 0f;
+
+    private final ScreenshotNotificationsController mNotificationsController;
+
+    private Context mContext;
+    private WindowManager mWindowManager;
+    private WindowManager.LayoutParams mWindowLayoutParams;
+    private Display mDisplay;
+    private DisplayMetrics mDisplayMetrics;
+
+    private Bitmap mScreenBitmap;
+    private View mScreenshotLayout;
+    private ScreenshotSelectorView mScreenshotSelectorView;
+    private ImageView mBackgroundView;
+    private ImageView mScreenshotView;
+    private ImageView mScreenshotFlash;
+
+    private AnimatorSet mScreenshotAnimation;
+
+    private float mBgPadding;
+    private float mBgPaddingScale;
+
+    private AsyncTask<Void, Void, Void> mSaveInBgTask;
+
+    private MediaActionSound mCameraSound;
+
+    /**
+     * @param context everything needs a context :(
+     */
+    @Inject
+    public GlobalScreenshotLegacy(
+            Context context, @Main Resources resources, LayoutInflater layoutInflater,
+            ScreenshotNotificationsController screenshotNotificationsController) {
+        mContext = context;
+        mNotificationsController = screenshotNotificationsController;
+
+        // Inflate the screenshot layout
+        mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot_legacy, null);
+        mBackgroundView = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy_background);
+        mScreenshotView = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy);
+
+        mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy_flash);
+        mScreenshotSelectorView = mScreenshotLayout.findViewById(
+                R.id.global_screenshot_legacy_selector);
+        mScreenshotLayout.setFocusable(true);
+        mScreenshotSelectorView.setFocusable(true);
+        mScreenshotSelectorView.setFocusableInTouchMode(true);
+        mScreenshotLayout.setOnTouchListener((v, event) -> {
+            // Intercept and ignore all touch events
+            return true;
+        });
+
+        // Setup the window that we are going to use
+        mWindowLayoutParams = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
+                WindowManager.LayoutParams.TYPE_SCREENSHOT,
+                WindowManager.LayoutParams.FLAG_FULLSCREEN
+                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
+                PixelFormat.TRANSLUCENT);
+        mWindowLayoutParams.setTitle("ScreenshotAnimation");
+        mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        mWindowLayoutParams.setFitWindowInsetsTypes(0 /* types */);
+        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        mDisplay = mWindowManager.getDefaultDisplay();
+        mDisplayMetrics = new DisplayMetrics();
+        mDisplay.getRealMetrics(mDisplayMetrics);
+
+        // Scale has to account for both sides of the bg
+        mBgPadding = (float) resources.getDimensionPixelSize(
+                R.dimen.global_screenshot_legacy_bg_padding);
+        mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels;
+
+
+        // Setup the Camera shutter sound
+        mCameraSound = new MediaActionSound();
+        mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
+    }
+
+    /**
+     * Creates a new worker thread and saves the screenshot to the media store.
+     */
+    private void saveScreenshotInWorkerThread(
+            Consumer<Uri> finisher,
+            @Nullable GlobalScreenshot.ActionsReadyListener actionsReadyListener) {
+        GlobalScreenshot.SaveImageInBackgroundData data =
+                new GlobalScreenshot.SaveImageInBackgroundData();
+        data.image = mScreenBitmap;
+        data.finisher = finisher;
+        data.mActionsReadyListener = actionsReadyListener;
+        if (mSaveInBgTask != null) {
+            mSaveInBgTask.cancel(false);
+        }
+
+        mNotificationsController.reset();
+        mNotificationsController.setImage(mScreenBitmap);
+        mNotificationsController.showSavingScreenshotNotification();
+
+        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data).execute();
+    }
+
+    /**
+     * Takes a screenshot of the current display and shows an animation.
+     */
+    private void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible,
+            boolean navBarVisible, Rect crop) {
+        int rot = mDisplay.getRotation();
+        int width = crop.width();
+        int height = crop.height();
+
+        // Take the screenshot
+        mScreenBitmap = SurfaceControl.screenshot(crop, width, height, rot);
+        if (mScreenBitmap == null) {
+            mNotificationsController.notifyScreenshotError(
+                    R.string.screenshot_failed_to_capture_text);
+            finisher.accept(null);
+            return;
+        }
+
+        // Optimizations
+        mScreenBitmap.setHasAlpha(false);
+        mScreenBitmap.prepareToDraw();
+
+        // Start the post-screenshot animation
+        startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
+                statusBarVisible, navBarVisible);
+    }
+
+    void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, boolean navBarVisible) {
+        mDisplay.getRealMetrics(mDisplayMetrics);
+        takeScreenshot(finisher, statusBarVisible, navBarVisible,
+                new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
+    }
+
+    /**
+     * Displays a screenshot selector
+     */
+    void takeScreenshotPartial(final Consumer<Uri> finisher, final boolean statusBarVisible,
+            final boolean navBarVisible) {
+        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+        mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                ScreenshotSelectorView view = (ScreenshotSelectorView) v;
+                switch (event.getAction()) {
+                    case MotionEvent.ACTION_DOWN:
+                        view.startSelection((int) event.getX(), (int) event.getY());
+                        return true;
+                    case MotionEvent.ACTION_MOVE:
+                        view.updateSelection((int) event.getX(), (int) event.getY());
+                        return true;
+                    case MotionEvent.ACTION_UP:
+                        view.setVisibility(View.GONE);
+                        mWindowManager.removeView(mScreenshotLayout);
+                        final Rect rect = view.getSelectionRect();
+                        if (rect != null) {
+                            if (rect.width() != 0 && rect.height() != 0) {
+                                // Need mScreenshotLayout to handle it after the view disappears
+                                mScreenshotLayout.post(() -> takeScreenshot(
+                                        finisher, statusBarVisible, navBarVisible, rect));
+                            }
+                        }
+
+                        view.stopSelection();
+                        return true;
+                }
+
+                return false;
+            }
+        });
+        mScreenshotLayout.post(new Runnable() {
+            @Override
+            public void run() {
+                mScreenshotSelectorView.setVisibility(View.VISIBLE);
+                mScreenshotSelectorView.requestFocus();
+            }
+        });
+    }
+
+    /**
+     * Cancels screenshot request
+     */
+    void stopScreenshot() {
+        // If the selector layer still presents on screen, we remove it and resets its state.
+        if (mScreenshotSelectorView.getSelectionRect() != null) {
+            mWindowManager.removeView(mScreenshotLayout);
+            mScreenshotSelectorView.stopSelection();
+        }
+    }
+
+    /**
+     * Clears current screenshot
+     */
+    private void clearScreenshot() {
+        if (mScreenshotLayout.isAttachedToWindow()) {
+            mWindowManager.removeView(mScreenshotLayout);
+        }
+
+        // Clear any references to the bitmap
+        mScreenBitmap = null;
+        mScreenshotView.setImageBitmap(null);
+        mBackgroundView.setVisibility(View.GONE);
+        mScreenshotView.setVisibility(View.GONE);
+        mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
+    }
+
+    /**
+     * Starts the animation after taking the screenshot
+     */
+    private void startAnimation(final Consumer<Uri> finisher, int w, int h,
+            boolean statusBarVisible, boolean navBarVisible) {
+        // If power save is on, show a toast so there is some visual indication that a screenshot
+        // has been taken.
+        PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        if (powerManager.isPowerSaveMode()) {
+            Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
+        }
+
+        // Add the view for the animation
+        mScreenshotView.setImageBitmap(mScreenBitmap);
+        mScreenshotLayout.requestFocus();
+
+        // Setup the animation with the screenshot just taken
+        if (mScreenshotAnimation != null) {
+            if (mScreenshotAnimation.isStarted()) {
+                mScreenshotAnimation.end();
+            }
+            mScreenshotAnimation.removeAllListeners();
+        }
+
+        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+        ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
+        ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h,
+                statusBarVisible, navBarVisible);
+        mScreenshotAnimation = new AnimatorSet();
+        mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
+        mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                // Save the screenshot once we have a bit of time now
+                saveScreenshotInWorkerThread(finisher, new GlobalScreenshot.ActionsReadyListener() {
+                    @Override
+                    void onActionsReady(Uri uri, List<Notification.Action> actions) {
+                        if (uri == null) {
+                            mNotificationsController.notifyScreenshotError(
+                                    R.string.screenshot_failed_to_capture_text);
+                        } else {
+                            mNotificationsController.showScreenshotActionsNotification(
+                                    uri, actions);
+                        }
+                    }
+                });
+                clearScreenshot();
+            }
+        });
+        mScreenshotLayout.post(() -> {
+            // Play the shutter sound to notify that we've taken a screenshot
+            mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+
+            mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            mScreenshotView.buildLayer();
+            mScreenshotAnimation.start();
+        });
+    }
+
+    private ValueAnimator createScreenshotDropInAnimation() {
+        final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
+                / SCREENSHOT_DROP_IN_DURATION);
+        final float flashDurationPct = 2f * flashPeakDurationPct;
+        final Interpolator flashAlphaInterpolator = new Interpolator() {
+            @Override
+            public float getInterpolation(float x) {
+                // Flash the flash view in and out quickly
+                if (x <= flashDurationPct) {
+                    return (float) Math.sin(Math.PI * (x / flashDurationPct));
+                }
+                return 0;
+            }
+        };
+        final Interpolator scaleInterpolator = new Interpolator() {
+            @Override
+            public float getInterpolation(float x) {
+                // We start scaling when the flash is at it's peak
+                if (x < flashPeakDurationPct) {
+                    return 0;
+                }
+                return (x - flashDurationPct) / (1f - flashDurationPct);
+            }
+        };
+
+        Resources r = mContext.getResources();
+        if ((r.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
+                == Configuration.UI_MODE_NIGHT_YES) {
+            mScreenshotView.getBackground().setTint(Color.BLACK);
+        } else {
+            mScreenshotView.getBackground().setTintList(null);
+        }
+
+        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+        anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mBackgroundView.setAlpha(0f);
+                mBackgroundView.setVisibility(View.VISIBLE);
+                mScreenshotView.setAlpha(0f);
+                mScreenshotView.setTranslationX(0f);
+                mScreenshotView.setTranslationY(0f);
+                mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);
+                mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);
+                mScreenshotView.setVisibility(View.VISIBLE);
+                mScreenshotFlash.setAlpha(0f);
+                mScreenshotFlash.setVisibility(View.VISIBLE);
+            }
+
+            @Override
+            public void onAnimationEnd(android.animation.Animator animation) {
+                mScreenshotFlash.setVisibility(View.GONE);
+            }
+        });
+        anim.addUpdateListener(new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float t = (Float) animation.getAnimatedValue();
+                float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
+                        - scaleInterpolator.getInterpolation(t)
+                        * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
+                mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
+                mScreenshotView.setAlpha(t);
+                mScreenshotView.setScaleX(scaleT);
+                mScreenshotView.setScaleY(scaleT);
+                mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t));
+            }
+        });
+        return anim;
+    }
+
+    private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,
+            boolean navBarVisible) {
+        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+        anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mBackgroundView.setVisibility(View.GONE);
+                mScreenshotView.setVisibility(View.GONE);
+                mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
+            }
+        });
+
+        if (!statusBarVisible || !navBarVisible) {
+            // There is no status bar/nav bar, so just fade the screenshot away in place
+            anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION);
+            anim.addUpdateListener(new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    float t = (Float) animation.getAnimatedValue();
+                    float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
+                            - t * (SCREENSHOT_DROP_IN_MIN_SCALE
+                            - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);
+                    mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
+                    mScreenshotView.setAlpha(1f - t);
+                    mScreenshotView.setScaleX(scaleT);
+                    mScreenshotView.setScaleY(scaleT);
+                }
+            });
+        } else {
+            // In the case where there is a status bar, animate to the origin of the bar (top-left)
+            final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION
+                    / SCREENSHOT_DROP_OUT_DURATION;
+            final Interpolator scaleInterpolator = new Interpolator() {
+                @Override
+                public float getInterpolation(float x) {
+                    if (x < scaleDurationPct) {
+                        // Decelerate, and scale the input accordingly
+                        return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f));
+                    }
+                    return 1f;
+                }
+            };
+
+            // Determine the bounds of how to scale
+            float halfScreenWidth = (w - 2f * mBgPadding) / 2f;
+            float halfScreenHeight = (h - 2f * mBgPadding) / 2f;
+            final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET;
+            final PointF finalPos = new PointF(
+                    -halfScreenWidth
+                            + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
+                    -halfScreenHeight
+                            + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);
+
+            // Animate the screenshot to the status bar
+            anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
+            anim.addUpdateListener(new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    float t = (Float) animation.getAnimatedValue();
+                    float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
+                            - scaleInterpolator.getInterpolation(t)
+                            * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE);
+                    mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
+                    mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t));
+                    mScreenshotView.setScaleX(scaleT);
+                    mScreenshotView.setScaleY(scaleT);
+                    mScreenshotView.setTranslationX(t * finalPos.x);
+                    mScreenshotView.setTranslationY(t * finalPos.y);
+                }
+            });
+        }
+        return anim;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 29b96a9..4f045d5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -16,15 +16,21 @@
 
 package com.android.systemui.screenshot;
 
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
+
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_CORNER_FLOW;
+
 import android.app.Service;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.provider.DeviceConfig;
 import android.util.Log;
 import android.view.WindowManager;
 
@@ -36,9 +42,10 @@
     private static final String TAG = "TakeScreenshotService";
 
     private final GlobalScreenshot mScreenshot;
+    private final GlobalScreenshotLegacy mScreenshotLegacy;
     private final UserManager mUserManager;
 
-    private Handler mHandler = new Handler() {
+    private Handler mHandler = new Handler(Looper.myLooper()) {
         @Override
         public void handleMessage(Message msg) {
             final Messenger callback = msg.replyTo;
@@ -59,12 +66,24 @@
                 return;
             }
 
+            // TODO (mkephart): clean up once notifications flow is fully deprecated
+            boolean useCornerFlow = DeviceConfig.getBoolean(
+                    NAMESPACE_SYSTEMUI, SCREENSHOT_CORNER_FLOW, false);
             switch (msg.what) {
                 case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
-                    mScreenshot.takeScreenshot(finisher, msg.arg1 > 0, msg.arg2 > 0);
+                    if (useCornerFlow) {
+                        mScreenshot.takeScreenshot(finisher);
+                    } else {
+                        mScreenshotLegacy.takeScreenshot(finisher, msg.arg1 > 0, msg.arg2 > 0);
+                    }
                     break;
                 case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
-                    mScreenshot.takeScreenshotPartial(finisher, msg.arg1 > 0, msg.arg2 > 0);
+                    if (useCornerFlow) {
+                        mScreenshot.takeScreenshotPartial(finisher);
+                    } else {
+                        mScreenshotLegacy.takeScreenshotPartial(
+                                finisher, msg.arg1 > 0, msg.arg2 > 0);
+                    }
                     break;
                 default:
                     Log.d(TAG, "Invalid screenshot option: " + msg.what);
@@ -73,8 +92,10 @@
     };
 
     @Inject
-    public TakeScreenshotService(GlobalScreenshot globalScreenshot, UserManager userManager) {
+    public TakeScreenshotService(GlobalScreenshot globalScreenshot,
+            GlobalScreenshotLegacy globalScreenshotLegacy, UserManager userManager) {
         mScreenshot = globalScreenshot;
+        mScreenshotLegacy = globalScreenshotLegacy;
         mUserManager = userManager;
     }
 
@@ -86,6 +107,7 @@
     @Override
     public boolean onUnbind(Intent intent) {
         if (mScreenshot != null) mScreenshot.stopScreenshot();
+        if (mScreenshotLegacy != null) mScreenshotLegacy.stopScreenshot();
         return true;
     }
 }
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/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
index 6a2774b..80b5b8a 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;
@@ -67,6 +67,7 @@
     private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
 
     private final Context mContext;
+    private final NotificationRowContentBinder mRowContentBinder;
     private final NotificationMessagingUtil mMessagingUtil;
     private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
             this::logNotificationExpansion;
@@ -79,7 +80,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;
@@ -90,6 +91,7 @@
             Context context,
             NotificationRemoteInputManager notificationRemoteInputManager,
             NotificationLockscreenUserManager notificationLockscreenUserManager,
+            NotificationRowContentBinder rowContentBinder,
             @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
             KeyguardBypassController keyguardBypassController,
             StatusBarStateController statusBarStateController,
@@ -98,6 +100,7 @@
             NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             NotificationLogger logger) {
         mContext = context;
+        mRowContentBinder = rowContentBinder;
         mMessagingUtil = new NotificationMessagingUtil(context);
         mNotificationRemoteInputManager = notificationRemoteInputManager;
         mNotificationLockscreenUserManager = notificationLockscreenUserManager;
@@ -124,7 +127,7 @@
         mOnAppOpsClickListener = mGutsManager::openGuts;
     }
 
-    public void setInflationCallback(NotificationContentInflater.InflationCallback callback) {
+    public void setInflationCallback(NotificationRowContentBinder.InflationCallback callback) {
         mInflationCallback = callback;
     }
 
@@ -163,19 +166,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);
-        mNotificationRemoteInputManager.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.
@@ -193,15 +183,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);
+        mNotificationRemoteInputManager.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/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 78eaf3e..452d1eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -17,7 +17,9 @@
 package com.android.systemui.statusbar.notification.people
 
 import android.app.Notification
+import android.content.Context
 import android.service.notification.StatusBarNotification
+import android.util.FeatureFlagUtils
 import javax.inject.Inject
 import javax.inject.Singleton
 
@@ -27,10 +29,16 @@
 
 @Singleton
 class PeopleNotificationIdentifierImpl @Inject constructor(
-    private val personExtractor: NotificationPersonExtractor
+    private val personExtractor: NotificationPersonExtractor,
+    private val context: Context
 ) : PeopleNotificationIdentifier {
 
     override fun isPeopleNotification(sbn: StatusBarNotification) =
-            sbn.notification.notificationStyle == Notification.MessagingStyle::class.java ||
+            (sbn.notification.notificationStyle == Notification.MessagingStyle::class.java &&
+                    (sbn.notification.shortcutId != null ||
+                            FeatureFlagUtils.isEnabled(
+                                    context,
+                                    FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ
+                            ))) ||
                     personExtractor.isPersonNotification(sbn)
 }
\ No newline at end of file
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..a6e5c2b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java
@@ -0,0 +1,110 @@
+/*
+ * 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.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) {
+        SparseArray<RemoteViews> contentViews = mNotifCachedContentViews.get(entry);
+        if (contentViews == null) {
+            return null;
+        }
+        return contentViews.get(flag);
+    }
+
+    @Override
+    public void putCachedView(
+            NotificationEntry entry,
+            @InflationFlag int flag,
+            RemoteViews remoteView) {
+        /**
+         * TODO: We should be more strict here in the future (i.e. throw an exception) if the
+         * content views aren't created. We don't do that right now because we have edge cases
+         * where we may bind/unbind content after a notification is removed.
+         */
+        SparseArray<RemoteViews> contentViews = mNotifCachedContentViews.get(entry);
+        if (contentViews == null) {
+            return;
+        }
+        contentViews.put(flag, remoteView);
+    }
+
+    @Override
+    public void removeCachedView(NotificationEntry entry, @InflationFlag int flag) {
+        SparseArray<RemoteViews> contentViews = mNotifCachedContentViews.get(entry);
+        if (contentViews == null) {
+            return;
+        }
+        contentViews.remove(flag);
+    }
+
+    @Override
+    public void clearCache(NotificationEntry entry) {
+        SparseArray<RemoteViews> contentViews = mNotifCachedContentViews.get(entry);
+        if (contentViews == null) {
+            return;
+        }
+        contentViews.clear();
+    }
+
+    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 30f22ac..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,
@@ -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
@@ -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,15 +712,15 @@
         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);
@@ -736,10 +754,10 @@
         @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/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/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/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index af0ef82..e5ae1aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -25,11 +25,11 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+
+import static java.lang.Thread.sleep;
 
 import android.app.AppOpsManager;
 import android.content.pm.PackageManager;
@@ -57,7 +57,6 @@
     private static final String TEST_PACKAGE_NAME = "test";
     private static final int TEST_UID = UserHandle.getUid(0, 0);
     private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0);
-    private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0);
 
     @Mock
     private AppOpsManager mAppOpsManager;
@@ -68,8 +67,6 @@
     @Mock
     private AppOpsControllerImpl.H mMockHandler;
     @Mock
-    private PermissionFlagsCache mFlagsCache;
-    @Mock
     private DumpController mDumpController;
 
     private AppOpsControllerImpl mController;
@@ -85,17 +82,9 @@
         // All permissions of TEST_UID and TEST_UID_OTHER are user sensitive. None of
         // TEST_UID_NON_USER_SENSITIVE are user sensitive.
         getContext().setMockPackageManager(mPackageManager);
-        when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
-                eq(UserHandle.getUserHandleForUid(TEST_UID)))).thenReturn(
-                PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
-        when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
-                eq(UserHandle.getUserHandleForUid(TEST_UID_OTHER)))).thenReturn(
-                PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
-        when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
-                eq(UserHandle.getUserHandleForUid(TEST_UID_NON_USER_SENSITIVE)))).thenReturn(0);
 
-        mController = new AppOpsControllerImpl(mContext, mTestableLooper.getLooper(), mFlagsCache,
-                mDumpController);
+        mController =
+                new AppOpsControllerImpl(mContext, mTestableLooper.getLooper(), mDumpController);
     }
 
     @Test
@@ -191,14 +180,6 @@
     }
 
     @Test
-    public void nonUserSensitiveOpsAreIgnored() {
-        mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
-                TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
-        assertEquals(0, mController.getActiveAppOpsForUser(
-                UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
-    }
-
-    @Test
     public void opNotedScheduledForRemoval() {
         mController.setBGHandler(mMockHandler);
         mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
@@ -251,4 +232,100 @@
         // Only one post to notify subscribers
         verify(mMockHandler, times(2)).scheduleRemoval(any(), anyLong());
     }
+
+    @Test
+    public void testActiveOpNotRemovedAfterNoted() throws InterruptedException {
+        // Replaces the timeout delay with 5 ms
+        AppOpsControllerImpl.H testHandler = mController.new H(mTestableLooper.getLooper()) {
+            @Override
+            public void scheduleRemoval(AppOpItem item, long timeToRemoval) {
+                super.scheduleRemoval(item, 5L);
+            }
+        };
+
+        mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+        mController.setBGHandler(testHandler);
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
+
+        mTestableLooper.processAllMessages();
+        List<AppOpItem> list = mController.getActiveAppOps();
+        verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+        // Duplicates are not removed between active and noted
+        assertEquals(2, list.size());
+
+        sleep(10L);
+
+        mTestableLooper.processAllMessages();
+
+        verify(mCallback, never()).onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false);
+        list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+    }
+
+    @Test
+    public void testNotedNotRemovedAfterActive() {
+        mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+        mTestableLooper.processAllMessages();
+        List<AppOpItem> list = mController.getActiveAppOps();
+        verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+        // Duplicates are not removed between active and noted
+        assertEquals(2, list.size());
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false);
+
+        mTestableLooper.processAllMessages();
+
+        verify(mCallback, never()).onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false);
+        list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+    }
+
+    @Test
+    public void testNotedAndActiveOnlyOneCall() {
+        mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+        mTestableLooper.processAllMessages();
+        verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+    }
+
+    @Test
+    public void testActiveAndNotedOnlyOneCall() {
+        mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
+
+        mTestableLooper.processAllMessages();
+        verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
deleted file mode 100644
index dc070de..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.appops
-
-import android.content.Context
-import android.content.pm.PackageManager
-import android.os.UserHandle
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mock
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class PermissionFlagsCacheTest : SysuiTestCase() {
-
-    companion object {
-        const val TEST_PERMISSION = "test_permission"
-        const val TEST_PACKAGE = "test_package"
-    }
-
-    @Mock
-    private lateinit var mPackageManager: PackageManager
-    @Mock
-    private lateinit var mUserHandle: UserHandle
-    private lateinit var flagsCache: TestPermissionFlagsCache
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        mContext.setMockPackageManager(mPackageManager)
-        flagsCache = TestPermissionFlagsCache(mContext)
-    }
-
-    @Test
-    fun testCallsPackageManager_exactlyOnce() {
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
-        flagsCache.time = CACHE_EXPIRATION - 1
-        verify(mPackageManager).getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
-    }
-
-    @Test
-    fun testCallsPackageManager_cacheExpired() {
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
-        flagsCache.time = CACHE_EXPIRATION + 1
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
-        verify(mPackageManager, times(2))
-                .getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
-    }
-
-    @Test
-    fun testCallsPackageMaanger_multipleKeys() {
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
-        flagsCache.getPermissionFlags(TEST_PERMISSION, "", mUserHandle)
-        verify(mPackageManager, times(2))
-                .getPermissionFlags(anyString(), anyString(), any())
-    }
-
-    private class TestPermissionFlagsCache(context: Context) : PermissionFlagsCache(context) {
-        var time = 0L
-
-        override fun getCurrentTime(): Long {
-            return time
-        }
-    }
-}
\ No newline at end of file
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 77659df..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,7 +359,6 @@
 
     private static void inflateAndWait(ExpandableNotificationRow row) throws Exception {
         CountDownLatch countDownLatch = new CountDownLatch(1);
-        row.getNotificationInflater().setInflateSynchronously(true);
         NotificationContentInflater.InflationCallback callback =
                 new NotificationContentInflater.InflationCallback() {
                     @Override
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 a60fd52..1e0179d 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
@@ -84,7 +84,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;
@@ -200,9 +203,15 @@
 
         mEntry.expandedIcon = mock(StatusBarIconView.class);
 
+        NotificationRowContentBinder contentBinder = new NotificationContentInflater(
+                mock(NotifRemoteViewCache.class),
+                mRemoteInputManager);
+
         NotificationRowBinderImpl notificationRowBinder =
                 new NotificationRowBinderImpl(mContext,
-                        mRemoteInputManager, mLockscreenUserManager,
+                        mRemoteInputManager,
+                        mLockscreenUserManager,
+                        contentBinder,
                         true, /* allowLongPress */
                         mock(KeyguardBypassController.class),
                         mock(StatusBarStateController.class),
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 f916fe5..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,11 +22,16 @@
 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;
@@ -35,16 +40,17 @@
 import android.os.Looper;
 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;
@@ -57,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;
@@ -73,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")
@@ -83,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
@@ -174,7 +187,9 @@
                 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
@@ -244,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)
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
index e99c2c5..5a71eb2 100644
--- a/packages/Tethering/AndroidManifest.xml
+++ b/packages/Tethering/AndroidManifest.xml
@@ -32,6 +32,7 @@
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USB" />
     <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
     <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 37e679d..ca290c6 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -1,7 +1,152 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
 <resources>
     <!--
     OEMs that wish to change the below settings must do so via a runtime resource overlay package
     and *NOT* by changing this file. This file is part of the tethering mainline module.
+    TODO: define two resources for each config item: a default_* resource and a config_* resource,
+    config_* is empty by default but may be overridden by RROs.
     -->
+    <!-- List of regexpressions describing the interface (if any) that represent tetherable
+         USB interfaces.  If the device doesn't want to support tethering over USB this should
+         be empty.  An example would be "usb.*" -->
+    <string-array translatable="false" name="config_tether_usb_regexs">
+        <item>"usb\\d"</item>
+        <item>"rndis\\d"</item>
+    </string-array>
+
+    <!-- List of regexpressions describing the interface (if any) that represent tetherable
+         Wifi interfaces.  If the device doesn't want to support tethering over Wifi this
+         should be empty.  An example would be "softap.*" -->
+    <string-array translatable="false" name="config_tether_wifi_regexs">
+        <item>"wlan\\d"</item>
+        <item>"softap\\d"</item>
+    </string-array>
+
+    <!-- List of regexpressions describing the interface (if any) that represent tetherable
+         Wifi P2P interfaces.  If the device doesn't want to support tethering over Wifi P2p this
+         should be empty.  An example would be "p2p-p2p.*" -->
+    <string-array translatable="false" name="config_tether_wifi_p2p_regexs">
+    </string-array>
+
+    <!-- List of regexpressions describing the interface (if any) that represent tetherable
+         bluetooth interfaces.  If the device doesn't want to support tethering over bluetooth this
+         should be empty. -->
+    <string-array translatable="false" name="config_tether_bluetooth_regexs">
+        <item>"bt-pan"</item>
+    </string-array>
+
+    <!-- Use the old dnsmasq DHCP server for tethering instead of the framework implementation. -->
+    <bool translatable="false" name="config_tether_enable_legacy_dhcp_server">false</bool>
+
+    <!-- Dhcp range (min, max) to use for tethering purposes -->
+    <string-array translatable="false" name="config_tether_dhcp_range">
+    </string-array>
+
+    <!-- Array of ConnectivityManager.TYPE_{BLUETOOTH, ETHERNET, MOBILE, MOBILE_DUN, MOBILE_HIPRI,
+         WIFI} values allowable for tethering.
+
+         Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
+         [1,7,0] for TYPE_WIFI, TYPE_BLUETOOTH, and TYPE_MOBILE.
+
+         This list is also modified by code within the framework, including:
+
+             - TYPE_ETHERNET (9) is prepended to this list, and
+
+             - the return value of TelephonyManager.isTetheringApnRequired()
+               determines how the array is further modified:
+
+                   * TRUE (DUN REQUIRED).
+                     TYPE_MOBILE is removed (if present).
+                     TYPE_MOBILE_HIPRI is removed (if present).
+                     TYPE_MOBILE_DUN is appended (if not already present).
+
+                   * FALSE (DUN NOT REQUIRED).
+                     TYPE_MOBILE_DUN is removed (if present).
+                     If both of TYPE_MOBILE{,_HIPRI} are not present:
+                        TYPE_MOBILE is appended.
+                        TYPE_MOBILE_HIPRI is appended.
+
+         For other changes applied to this list, now and in the future, see
+         com.android.server.connectivity.tethering.TetheringConfiguration.
+
+         Note also: the order of this is important. The first upstream type
+         for which a satisfying network exists is used.
+    -->
+    <integer-array translatable="false" name="config_tether_upstream_types">
+    </integer-array>
+
+    <!-- When true, the tethering upstream network follows the current default
+         Internet network (except when the current default network is mobile,
+         in which case a DUN network will be used if required).
+
+         When true, overrides the config_tether_upstream_types setting above.
+    -->
+    <bool translatable="false" name="config_tether_upstream_automatic">true</bool>
+
+
+    <!-- If the mobile hotspot feature requires provisioning, a package name and class name
+         can be provided to launch a supported application that provisions the devices.
+         EntitlementManager will send an inent to Settings with the specified package name and
+         class name in extras to launch provision app.
+         TODO: note what extras here.
+
+         See EntitlementManager#runUiTetherProvisioning and
+         packages/apps/Settings/src/com/android/settings/network/TetherProvisioningActivity.java
+         for more details.
+
+         For ui-less/periodic recheck support see config_mobile_hotspot_provision_app_no_ui
+        -->
+    <!-- The first element is the package name and the second element is the class name
+         of the provisioning app -->
+    <string-array translatable="false" name="config_mobile_hotspot_provision_app">
+    <!--
+        <item>com.example.provisioning</item>
+        <item>com.example.provisioning.Activity</item>
+    -->
+    </string-array>
+
+    <!-- If the mobile hotspot feature requires provisioning, an action can be provided
+         that will be broadcast in non-ui cases for checking the provisioning status.
+         EntitlementManager will pass the specified name to Settings and Settings would
+         launch provisioning app by sending an intent with the package name.
+
+         A second broadcast, action defined by config_mobile_hotspot_provision_response,
+         will be sent back to notify if provisioning succeeded or not.  The response will
+         match that of the activity in config_mobile_hotspot_provision_app, but instead
+         contained within the int extra "EntitlementResult".
+         TODO: provide the system api for "EntitlementResult" extra and note it here.
+
+         See EntitlementManager#runSilentTetherProvisioning and
+         packages/apps/Settings/src/com/android/settings/wifi/tether/TetherService.java for more
+         details.
+        -->
+    <string translatable="false" name="config_mobile_hotspot_provision_app_no_ui"></string>
+
+    <!-- Sent in response to a provisioning check. The caller must hold the
+         permission android.permission.TETHER_PRIVILEGED for Settings to
+         receive this response.
+
+         See config_mobile_hotspot_provision_response
+         -->
+    <string translatable="false" name="config_mobile_hotspot_provision_response"></string>
+
+    <!-- Number of hours between each background provisioning call -->
+    <integer translatable="false" name="config_mobile_hotspot_provision_check_period">24</integer>
+
+    <!-- ComponentName of the service used to run no ui tether provisioning. -->
+    <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string>
 </resources>
diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml
new file mode 100644
index 0000000..e089d9d
--- /dev/null
+++ b/packages/Tethering/res/values/overlayable.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <overlayable name="TetheringConfig">
+        <policy type="product|system|vendor">
+            <item type="array" name="config_tether_usb_regexs"/>
+            <item type="array" name="config_tether_wifi_regexs"/>
+            <item type="array" name="config_tether_wifi_p2p_regexs"/>
+            <item type="array" name="config_tether_bluetooth_regexs"/>
+            <item type="array" name="config_tether_dhcp_range"/>
+            <item type="bool" name="config_tether_enable_legacy_dhcp_server"/>
+            <item type="array" name="config_tether_upstream_types"/>
+            <item type="bool" name="config_tether_upstream_automatic"/>
+            <!-- Configuration values for tethering entitlement check -->
+            <item type="array" name="config_mobile_hotspot_provision_app"/>
+            <item type="string" name="config_mobile_hotspot_provision_app_no_ui"/>
+            <item type="string" name="config_mobile_hotspot_provision_response"/>
+            <item type="integer" name="config_mobile_hotspot_provision_check_period"/>
+            <item type="string" name="config_wifi_tether_enable"/>
+        </policy>
+    </overlayable>
+</resources>
diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml
index ca866a9..792bce9 100644
--- a/packages/Tethering/res/values/strings.xml
+++ b/packages/Tethering/res/values/strings.xml
@@ -1,4 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
 <resources>
     <!-- Shown when the device is tethered -->
     <!-- Strings for tethered notification title [CHAR LIMIT=200] -->
@@ -9,8 +23,11 @@
     <!-- This notification is shown when tethering has been disabled on a user's device.
     The device is managed by the user's employer. Tethering can't be turned on unless the
     IT administrator allows it. The noun "admin" is another reference for "IT administrator." -->
-    <!-- Strings for tether disabling notification title  [CHAR LIMIT=200] -->
+    <!-- Strings for tether disabling notification title [CHAR LIMIT=200] -->
     <string name="disable_tether_notification_title">Tethering is disabled</string>
-    <!-- Strings for tether disabling notification message  [CHAR LIMIT=200] -->
+    <!-- Strings for tether disabling notification message [CHAR LIMIT=200] -->
     <string name="disable_tether_notification_message">Contact your admin for details</string>
+
+    <!-- Strings for tether notification channel name [CHAR LIMIT=200] -->
+    <string name="notification_channel_tethering_status">Hotspot &amp; tethering status</string>
 </resources>
\ No newline at end of file
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 d6abfb9..038d7ae 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -50,6 +50,7 @@
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
@@ -106,8 +107,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.MessageUtils;
 import com.android.internal.util.State;
@@ -663,19 +662,19 @@
 
         if (usbTethered) {
             if (wifiTethered || bluetoothTethered) {
-                showTetheredNotification(SystemMessage.NOTE_TETHER_GENERAL);
+                showTetheredNotification(R.drawable.stat_sys_tether_general);
             } else {
-                showTetheredNotification(SystemMessage.NOTE_TETHER_USB);
+                showTetheredNotification(R.drawable.stat_sys_tether_usb);
             }
         } else if (wifiTethered) {
             if (bluetoothTethered) {
-                showTetheredNotification(SystemMessage.NOTE_TETHER_GENERAL);
+                showTetheredNotification(R.drawable.stat_sys_tether_general);
             } else {
                 /* We now have a status bar icon for WifiTethering, so drop the notification */
                 clearTetheredNotification();
             }
         } else if (bluetoothTethered) {
-            showTetheredNotification(SystemMessage.NOTE_TETHER_BLUETOOTH);
+            showTetheredNotification(R.drawable.stat_sys_tether_bluetooth);
         } else {
             clearTetheredNotification();
         }
@@ -688,30 +687,22 @@
     @VisibleForTesting
     protected void showTetheredNotification(int id, boolean tetheringOn) {
         NotificationManager notificationManager =
-                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+                (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0)
+                        .getSystemService(Context.NOTIFICATION_SERVICE);
         if (notificationManager == null) {
             return;
         }
-        int icon = 0;
-        switch(id) {
-            case SystemMessage.NOTE_TETHER_USB:
-                icon = R.drawable.stat_sys_tether_usb;
-                break;
-            case SystemMessage.NOTE_TETHER_BLUETOOTH:
-                icon = R.drawable.stat_sys_tether_bluetooth;
-                break;
-            case SystemMessage.NOTE_TETHER_GENERAL:
-            default:
-                icon = R.drawable.stat_sys_tether_general;
-                break;
-        }
+        final NotificationChannel channel = new NotificationChannel(
+                "TETHERING_STATUS",
+                mContext.getResources().getString(R.string.notification_channel_tethering_status),
+                NotificationManager.IMPORTANCE_LOW);
+        notificationManager.createNotificationChannel(channel);
 
         if (mLastNotificationId != 0) {
-            if (mLastNotificationId == icon) {
+            if (mLastNotificationId == id) {
                 return;
             }
-            notificationManager.cancelAsUser(null, mLastNotificationId,
-                    UserHandle.ALL);
+            notificationManager.cancel(null, mLastNotificationId);
             mLastNotificationId = 0;
         }
 
@@ -719,8 +710,8 @@
         intent.setClassName("com.android.settings", "com.android.settings.TetherSettings");
         intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
 
-        PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0,
-                null, UserHandle.CURRENT);
+        PendingIntent pi = PendingIntent.getActivity(
+                mContext.createContextAsUser(UserHandle.CURRENT, 0), 0, intent, 0, null);
 
         Resources r = mContext.getResources();
         final CharSequence title;
@@ -735,32 +726,31 @@
         }
 
         if (mTetheredNotificationBuilder == null) {
-            mTetheredNotificationBuilder = new Notification.Builder(mContext,
-                    SystemNotificationChannels.NETWORK_STATUS);
+            mTetheredNotificationBuilder = new Notification.Builder(mContext, channel.getId());
             mTetheredNotificationBuilder.setWhen(0)
                     .setOngoing(true)
                     .setColor(mContext.getColor(
-                            com.android.internal.R.color.system_notification_accent_color))
+                            android.R.color.system_notification_accent_color))
                     .setVisibility(Notification.VISIBILITY_PUBLIC)
                     .setCategory(Notification.CATEGORY_STATUS);
         }
-        mTetheredNotificationBuilder.setSmallIcon(icon)
+        mTetheredNotificationBuilder.setSmallIcon(id)
                 .setContentTitle(title)
                 .setContentText(message)
                 .setContentIntent(pi);
         mLastNotificationId = id;
 
-        notificationManager.notifyAsUser(null, mLastNotificationId,
-                mTetheredNotificationBuilder.buildInto(new Notification()), UserHandle.ALL);
+        notificationManager.notify(null, mLastNotificationId,
+                mTetheredNotificationBuilder.buildInto(new Notification()));
     }
 
     @VisibleForTesting
     protected void clearTetheredNotification() {
         NotificationManager notificationManager =
-                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+                (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0)
+                        .getSystemService(Context.NOTIFICATION_SERVICE);
         if (notificationManager != null && mLastNotificationId != 0) {
-            notificationManager.cancelAsUser(null, mLastNotificationId,
-                    UserHandle.ALL);
+            notificationManager.cancel(null, mLastNotificationId);
             mLastNotificationId = 0;
         }
     }
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 397ba8a..dbe7892 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -21,7 +21,7 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
-import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
+import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
 
 import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
 import static com.android.internal.R.array.config_tether_bluetooth_regexs;
@@ -33,13 +33,13 @@
 import static com.android.internal.R.bool.config_tether_upstream_automatic;
 import static com.android.internal.R.integer.config_mobile_hotspot_provision_check_period;
 import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
+import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
 
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.TetheringConfigurationParcel;
 import android.net.util.SharedLog;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -84,6 +84,12 @@
 
     private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
 
+    /**
+     * Use the old dnsmasq DHCP server for tethering instead of the framework implementation.
+     */
+    public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
+            "tether_enable_legacy_dhcp_server";
+
     public final String[] tetherableUsbRegexs;
     public final String[] tetherableWifiRegexs;
     public final String[] tetherableWifiP2pRegexs;
@@ -122,7 +128,7 @@
 
         legacyDhcpRanges = getLegacyDhcpRanges(res);
         defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
-        enableLegacyDhcpServer = getEnableLegacyDhcpServer(ctx);
+        enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
 
         provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app);
         provisioningAppNoUi = getProvisioningAppNoUi(res);
@@ -332,10 +338,14 @@
         }
     }
 
-    private static boolean getEnableLegacyDhcpServer(Context ctx) {
-        final ContentResolver cr = ctx.getContentResolver();
-        final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
-        return intVal != 0;
+    private boolean getEnableLegacyDhcpServer(final Resources res) {
+        return getResourceBoolean(res, config_tether_enable_legacy_dhcp_server)
+                || getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER);
+    }
+
+    @VisibleForTesting
+    protected boolean getDeviceConfigBoolean(final String name) {
+        return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, false /** defaultValue */);
     }
 
     private Resources getResources(Context ctx, int subId) {
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 5692a6f..2875f71 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -23,6 +23,8 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 
@@ -215,19 +217,28 @@
             mLog.e("registerMobileNetworkRequest() already registered");
             return;
         }
-        // The following use of the legacy type system cannot be removed until
-        // after upstream selection no longer finds networks by legacy type.
-        // See also http://b/34364553 .
-        final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
 
-        final NetworkRequest mobileUpstreamRequest = new NetworkRequest.Builder()
-                .setCapabilities(networkCapabilitiesForType(legacyType))
-                .build();
+        final NetworkRequest mobileUpstreamRequest;
+        if (mDunRequired) {
+            mobileUpstreamRequest = new NetworkRequest.Builder()
+                    .addCapability(NET_CAPABILITY_DUN)
+                    .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+                    .addTransportType(TRANSPORT_CELLULAR).build();
+        } else {
+            mobileUpstreamRequest = new NetworkRequest.Builder()
+                    .addCapability(NET_CAPABILITY_INTERNET)
+                    .addTransportType(TRANSPORT_CELLULAR).build();
+        }
 
         // The existing default network and DUN callbacks will be notified.
         // Therefore, to avoid duplicate notifications, we only register a no-op.
         mMobileNetworkCallback = new UpstreamNetworkCallback(CALLBACK_MOBILE_REQUEST);
 
+        // The following use of the legacy type system cannot be removed until
+        // upstream selection no longer finds networks by legacy type.
+        // See also http://b/34364553 .
+        final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
+
         // TODO: Change the timeout from 0 (no onUnavailable callback) to some
         // moderate callback timeout. This might be useful for updating some UI.
         // Additionally, we log a message to aid in any subsequent debugging.
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 66eba9a..79bba7f 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
@@ -22,10 +22,12 @@
 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.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -39,7 +41,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.util.SharedLog;
@@ -49,9 +50,8 @@
 import android.os.ResultReceiver;
 import android.os.SystemProperties;
 import android.os.test.TestLooper;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
 import android.telephony.CarrierConfigManager;
-import android.test.mock.MockContentResolver;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -60,7 +60,6 @@
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.internal.util.test.BroadcastInterceptingContext;
-import com.android.internal.util.test.FakeSettingsProvider;
 
 import org.junit.After;
 import org.junit.Before;
@@ -94,7 +93,6 @@
     private final PersistableBundle mCarrierConfig = new PersistableBundle();
     private final TestLooper mLooper = new TestLooper();
     private Context mMockContext;
-    private MockContentResolver mContentResolver;
 
     private TestStateMachine mSM;
     private WrappedEntitlementManager mEnMgr;
@@ -110,11 +108,6 @@
         public Resources getResources() {
             return mResources;
         }
-
-        @Override
-        public ContentResolver getContentResolver() {
-            return mContentResolver;
-        }
     }
 
     public class WrappedEntitlementManager extends EntitlementManager {
@@ -151,13 +144,17 @@
         MockitoAnnotations.initMocks(this);
         mMockingSession = mockitoSession()
                 .initMocks(this)
-                .spyStatic(SystemProperties.class)
+                .mockStatic(SystemProperties.class)
+                .mockStatic(DeviceConfig.class)
                 .strictness(Strictness.WARN)
                 .startMocking();
         // Don't disable tethering provisioning unless requested.
         doReturn(false).when(
                 () -> SystemProperties.getBoolean(
                 eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean()));
+        doReturn(false).when(
+                () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
+                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
 
         when(mResources.getStringArray(R.array.config_tether_dhcp_range))
             .thenReturn(new String[0]);
@@ -169,10 +166,9 @@
             .thenReturn(new String[0]);
         when(mResources.getIntArray(R.array.config_tether_upstream_types))
             .thenReturn(new int[0]);
+        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
         when(mLog.forSubComponent(anyString())).thenReturn(mLog);
 
-        mContentResolver = new MockContentResolver();
-        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         mMockContext = new MockContext(mContext);
         mSM = new TestStateMachine();
         mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE);
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index 7799da4..ef97ad4 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -21,40 +21,44 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
 import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
+import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
 import static com.android.internal.R.array.config_tether_bluetooth_regexs;
 import static com.android.internal.R.array.config_tether_dhcp_range;
 import static com.android.internal.R.array.config_tether_upstream_types;
 import static com.android.internal.R.array.config_tether_usb_regexs;
 import static com.android.internal.R.array.config_tether_wifi_regexs;
+import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.when;
 
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.util.SharedLog;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
 import android.telephony.TelephonyManager;
-import android.test.mock.MockContentResolver;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.test.BroadcastInterceptingContext;
-import com.android.internal.util.test.FakeSettingsProvider;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
 
 import java.util.Arrays;
 import java.util.Iterator;
@@ -69,9 +73,10 @@
     @Mock private TelephonyManager mTelephonyManager;
     @Mock private Resources mResources;
     @Mock private Resources mResourcesForSubId;
-    private MockContentResolver mContentResolver;
     private Context mMockContext;
     private boolean mHasTelephonyManager;
+    private boolean mEnableLegacyDhcpServer;
+    private MockitoSession mMockingSession;
 
     private class MockTetheringConfiguration extends TetheringConfiguration {
         MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
@@ -101,16 +106,20 @@
             }
             return super.getSystemService(name);
         }
-
-        @Override
-        public ContentResolver getContentResolver() {
-            return mContentResolver;
-        }
     }
 
     @Before
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
+        // TODO: use a dependencies class instead of mock statics.
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .mockStatic(DeviceConfig.class)
+                .strictness(Strictness.WARN)
+                .startMocking();
+        doReturn(false).when(
+                () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
+                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+
         when(mResources.getStringArray(config_tether_dhcp_range)).thenReturn(new String[0]);
         when(mResources.getStringArray(config_tether_usb_regexs)).thenReturn(new String[0]);
         when(mResources.getStringArray(config_tether_wifi_regexs))
@@ -119,10 +128,15 @@
         when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
         when(mResources.getStringArray(config_mobile_hotspot_provision_app))
                 .thenReturn(new String[0]);
-        mContentResolver = new MockContentResolver();
-        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
         mHasTelephonyManager = true;
         mMockContext = new MockContext(mContext);
+        mEnableLegacyDhcpServer = false;
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mMockingSession.finishMocking();
     }
 
     private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) {
@@ -268,19 +282,35 @@
 
     @Test
     public void testNewDhcpServerDisabled() {
-        Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
+        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true);
+        doReturn(false).when(
+                () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
+                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
 
-        final TetheringConfiguration cfg = new TetheringConfiguration(
-                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
-        assertTrue(cfg.enableLegacyDhcpServer);
+        final TetheringConfiguration enableByRes =
+                new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertTrue(enableByRes.enableLegacyDhcpServer);
+
+        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+        doReturn(true).when(
+                () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
+                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+
+        final TetheringConfiguration enableByDevConfig =
+                new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertTrue(enableByDevConfig.enableLegacyDhcpServer);
     }
 
     @Test
     public void testNewDhcpServerEnabled() {
-        Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
+        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+        doReturn(false).when(
+                () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
+                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
 
-        final TetheringConfiguration cfg = new TetheringConfiguration(
-                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        final TetheringConfiguration cfg =
+                new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+
         assertFalse(cfg.enableLegacyDhcpServer);
     }
 
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 7af48a8..e1fe3bf 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
@@ -35,9 +35,10 @@
 import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
 import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
-import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
+import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -224,6 +225,11 @@
             if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE;
             return super.getSystemServiceName(serviceClass);
         }
+
+        @Override
+        public Context createContextAsUser(UserHandle user, int flags) {
+            return mContext;
+        }
     }
 
     public class MockIpServerDependencies extends IpServer.Dependencies {
@@ -265,6 +271,11 @@
         }
 
         @Override
+        protected boolean getDeviceConfigBoolean(final String name) {
+            return false;
+        }
+
+        @Override
         protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
             return mResources;
         }
@@ -423,6 +434,7 @@
                 .thenReturn(new int[0]);
         when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
                 .thenReturn(false);
+        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
         when(mNetd.interfaceGetList())
                 .thenReturn(new String[] {
                         TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME});
@@ -432,9 +444,9 @@
                 .thenReturn(true);
 
         mServiceContext = new TestContext(mContext);
+        when(mContext.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(null);
         mContentResolver = new MockContentResolver(mServiceContext);
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
-        Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
         mIntents = new Vector<>();
         mBroadcastReceiver = new BroadcastReceiver() {
             @Override
@@ -686,7 +698,7 @@
 
     @Test
     public void workingMobileUsbTethering_IPv4LegacyDhcp() {
-        Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
+        when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true);
         sendConfigurationChanged();
         final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
         runUsbTethering(upstreamState);
diff --git a/proto/Android.bp b/proto/Android.bp
index 65bccbb..7cf6ce7 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -27,3 +27,8 @@
     srcs: ["src/metrics_constants/metrics_constants.proto"],
     sdk_version: "system_current",
 }
+
+filegroup {
+    name: "system-messages-proto-src",
+    srcs: ["src/system_messages.proto"],
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index cbff6bd..37ac3ec 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -35,6 +35,7 @@
 import android.util.Slog;
 import android.view.Display;
 
+import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -264,6 +265,24 @@
     }
 
     @Override
+    public boolean switchToInputMethod(String imeId) {
+        synchronized (mLock) {
+            if (!hasRightsToCurrentUserLocked()) {
+                return false;
+            }
+        }
+        final boolean result;
+        final int callingUserId = UserHandle.getCallingUserId();
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            result = InputMethodManagerInternal.get().switchToInputMethod(imeId, callingUserId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return result;
+    }
+
+    @Override
     public boolean isAccessibilityButtonAvailable() {
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 7dd4a70..7a8a112 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -297,6 +297,11 @@
         }
 
         @Override
+        public boolean switchToInputMethod(String imeId) {
+            return false;
+        }
+
+        @Override
         public boolean isAccessibilityButtonAvailable() {
             return false;
         }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 03d9626..5405fc7 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -331,8 +331,8 @@
     }
 
     @Override // from SystemService
-    public boolean isSupported(UserInfo userInfo) {
-        return userInfo.isFull() || userInfo.isManagedProfile();
+    public boolean isSupportedUser(TargetUser user) {
+        return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile();
     }
 
     @Override // from SystemService
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f34b5e7..7d354d2 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -162,6 +162,9 @@
     /** uid the session is for */
     public final int uid;
 
+    /** user id the session is for */
+    public final int userId;
+
     /** ID of the task associated with this session's activity */
     public final int taskId;
 
@@ -613,7 +616,7 @@
             int newState, int flags) {
         if (isInlineSuggestionsEnabled()) {
             mInlineSuggestionsRequestCallback = new InlineSuggestionsRequestCallbackImpl();
-            mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
+            mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(userId,
                     mComponentName, mCurrentViewId, mInlineSuggestionsRequestCallback);
         }
 
@@ -759,6 +762,7 @@
         mFlags = flags;
         this.taskId = taskId;
         this.uid = uid;
+        this.userId = userId;
         mStartTime = SystemClock.elapsedRealtime();
         mService = service;
         mLock = lock;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d45a54e..b13bef2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1406,10 +1406,7 @@
         long oldId = Binder.clearCallingIdentity();
         final int[] userIds;
         try {
-            userIds =
-                    mContext
-                            .getSystemService(UserManager.class)
-                            .getProfileIds(callingUserId, false);
+            userIds = getUserManager().getProfileIds(callingUserId, false);
         } finally {
             Binder.restoreCallingIdentity(oldId);
         }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 27824af..8eca62a 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -175,8 +175,8 @@
     }
 
     @Override // from SystemService
-    public boolean isSupported(UserInfo userInfo) {
-        return userInfo.isFull() || userInfo.isManagedProfile();
+    public boolean isSupportedUser(TargetUser user) {
+        return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile();
     }
 
     @Override // from SystemService
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 76572d3..368416b 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -41,6 +41,7 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
 import java.util.List;
 import java.util.function.Consumer;
 
@@ -478,10 +479,12 @@
      *                            will be disabled. Pass in null or an empty list to disable
      *                            all overlays. The order of the items is significant if several
      *                            overlays modify the same resource.
+     * @param outUpdatedPackageNames An output list that contains the package names of packages
+     *                               affected by the update of enabled overlays.
      * @return true if all packages names were known by the package manager, false otherwise
      */
     public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName,
-            List<String> overlayPackageNames);
+            List<String> overlayPackageNames, Collection<String> outUpdatedPackageNames);
 
     /**
      * Resolves an activity intent, allowing instant apps to be resolved.
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 73b6c7a..0f2fb92 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -208,6 +208,7 @@
     AppWakeupHistory mAppWakeupHistory;
     ClockReceiver mClockReceiver;
     final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
+    IBinder.DeathRecipient mListenerDeathRecipient;
     Intent mTimeTickIntent;
     IAlarmListener mTimeTickTrigger;
     PendingIntent mDateChangeSender;
@@ -1447,6 +1448,18 @@
     public void onStart() {
         mInjector.init();
 
+        mListenerDeathRecipient = new IBinder.DeathRecipient() {
+            @Override
+            public void binderDied() {
+            }
+
+            @Override
+            public void binderDied(IBinder who) {
+                final IAlarmListener listener = IAlarmListener.Stub.asInterface(who);
+                removeImpl(null, listener);
+            }
+        };
+
         synchronized (mLock) {
             mHandler = new AlarmHandler();
             mConstants = new Constants(mHandler);
@@ -1653,6 +1666,15 @@
             return;
         }
 
+        if (directReceiver != null) {
+            try {
+                directReceiver.asBinder().linkToDeath(mListenerDeathRecipient, 0);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Dropping unreachable alarm listener " + listenerTag);
+                return;
+            }
+        }
+
         // Sanity check the window length.  This will catch people mistakenly
         // trying to pass an end-of-window timestamp rather than a duration.
         if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
@@ -2079,8 +2101,9 @@
         @Override
         public long currentNetworkTimeMillis() {
             final NtpTrustedTime time = NtpTrustedTime.getInstance(getContext());
-            if (time.hasCache()) {
-                return time.currentTimeMillis();
+            NtpTrustedTime.TimeResult ntpResult = time.getCachedTimeResult();
+            if (ntpResult != null) {
+                return ntpResult.currentTimeMillis();
             } else {
                 throw new ParcelableException(new DateTimeException("Missing NTP fix"));
             }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c2e32d3..f4962e5 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -84,12 +84,12 @@
 import android.net.NattSocketKeepalive;
 import android.net.Network;
 import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
 import android.net.NetworkFactory;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkMisc;
 import android.net.NetworkMonitorManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkProvider;
@@ -364,10 +364,10 @@
     private static final int EVENT_PROXY_HAS_CHANGED = 16;
 
     /**
-     * used internally when registering NetworkFactories
-     * obj = NetworkFactoryInfo
+     * used internally when registering NetworkProviders
+     * obj = NetworkProviderInfo
      */
-    private static final int EVENT_REGISTER_NETWORK_FACTORY = 17;
+    private static final int EVENT_REGISTER_NETWORK_PROVIDER = 17;
 
     /**
      * used internally when registering NetworkAgents
@@ -403,10 +403,10 @@
     private static final int EVENT_RELEASE_NETWORK_REQUEST = 22;
 
     /**
-     * used internally when registering NetworkFactories
+     * used internally when registering NetworkProviders
      * obj = Messenger
      */
-    private static final int EVENT_UNREGISTER_NETWORK_FACTORY = 23;
+    private static final int EVENT_UNREGISTER_NETWORK_PROVIDER = 23;
 
     /**
      * used internally to expire a wakelock when transitioning
@@ -2394,9 +2394,9 @@
             return;
         }
 
-        pw.print("NetworkFactories for:");
-        for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
-            pw.print(" " + nfi.name);
+        pw.print("NetworkProviders for:");
+        for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+            pw.print(" " + npi.name);
         }
         pw.println();
         pw.println();
@@ -2631,8 +2631,8 @@
                     if (nai.everConnected) {
                         loge("ERROR: cannot call explicitlySelected on already-connected network");
                     }
-                    nai.networkMisc.explicitlySelected = toBool(msg.arg1);
-                    nai.networkMisc.acceptUnvalidated = toBool(msg.arg1) && toBool(msg.arg2);
+                    nai.networkAgentConfig.explicitlySelected = toBool(msg.arg1);
+                    nai.networkAgentConfig.acceptUnvalidated = toBool(msg.arg1) && toBool(msg.arg2);
                     // Mark the network as temporarily accepting partial connectivity so that it
                     // will be validated (and possibly become default) even if it only provides
                     // partial internet access. Note that if user connects to partial connectivity
@@ -2640,7 +2640,7 @@
                     // out of wifi coverage) and if the same wifi is available again, the device
                     // will auto connect to this wifi even though the wifi has "no internet".
                     // TODO: Evaluate using a separate setting in IpMemoryStore.
-                    nai.networkMisc.acceptPartialConnectivity = toBool(msg.arg2);
+                    nai.networkAgentConfig.acceptPartialConnectivity = toBool(msg.arg2);
                     break;
                 }
                 case NetworkAgent.EVENT_SOCKET_KEEPALIVE: {
@@ -2672,10 +2672,10 @@
                         }
                         // Only show the notification when the private DNS is broken and the
                         // PRIVATE_DNS_BROKEN notification hasn't shown since last valid.
-                        if (privateDnsBroken && !nai.networkMisc.hasShownBroken) {
+                        if (privateDnsBroken && !nai.networkAgentConfig.hasShownBroken) {
                             showNetworkNotification(nai, NotificationType.PRIVATE_DNS_BROKEN);
                         }
-                        nai.networkMisc.hasShownBroken = privateDnsBroken;
+                        nai.networkAgentConfig.hasShownBroken = privateDnsBroken;
                     } else if (nai.networkCapabilities.isPrivateDnsBroken()) {
                         // If probePrivateDnsCompleted is false but nai.networkCapabilities says
                         // private DNS is broken, it means this network is being reevaluated.
@@ -2685,7 +2685,7 @@
                         nai.networkCapabilities.setPrivateDnsBroken(false);
                         final int oldScore = nai.getCurrentScore();
                         updateCapabilities(oldScore, nai, nai.networkCapabilities);
-                        nai.networkMisc.hasShownBroken = false;
+                        nai.networkAgentConfig.hasShownBroken = false;
                     }
                     break;
                 }
@@ -2727,7 +2727,7 @@
                         nai.lastValidated = valid;
                         nai.everValidated |= valid;
                         updateCapabilities(oldScore, nai, nai.networkCapabilities);
-                        // If score has changed, rebroadcast to NetworkFactories. b/17726566
+                        // If score has changed, rebroadcast to NetworkProviders. b/17726566
                         if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
                         if (valid) {
                             handleFreshlyValidatedNetwork(nai);
@@ -2744,7 +2744,7 @@
                             // If network becomes valid, the hasShownBroken should be reset for
                             // that network so that the notification will be fired when the private
                             // DNS is broken again.
-                            nai.networkMisc.hasShownBroken = false;
+                            nai.networkAgentConfig.hasShownBroken = false;
                         }
                     } else if (partialConnectivityChanged) {
                         updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
@@ -2803,9 +2803,10 @@
                             loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
                             break;
                         }
-                        if (!nai.networkMisc.provisioningNotificationDisabled) {
+                        if (!nai.networkAgentConfig.provisioningNotificationDisabled) {
                             mNotifier.showNotification(netId, NotificationType.SIGN_IN, nai, null,
-                                    (PendingIntent) msg.obj, nai.networkMisc.explicitlySelected);
+                                    (PendingIntent) msg.obj,
+                                    nai.networkAgentConfig.explicitlySelected);
                         }
                     }
                     break;
@@ -2842,6 +2843,7 @@
             return true;
         }
 
+        // TODO: delete when direct use of registerNetworkFactory is no longer supported.
         private boolean maybeHandleNetworkFactoryMessage(Message msg) {
             switch (msg.what) {
                 default:
@@ -3031,16 +3033,16 @@
     private void handleAsyncChannelHalfConnect(Message msg) {
         ensureRunningOnConnectivityServiceThread();
         final AsyncChannel ac = (AsyncChannel) msg.obj;
-        if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {
+        if (mNetworkProviderInfos.containsKey(msg.replyTo)) {
             if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
                 if (VDBG) log("NetworkFactory connected");
                 // Finish setting up the full connection
-                NetworkFactoryInfo nfi = mNetworkFactoryInfos.get(msg.replyTo);
-                nfi.completeConnection();
-                sendAllRequestsToFactory(nfi);
+                NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
+                npi.completeConnection();
+                sendAllRequestsToProvider(npi);
             } else {
                 loge("Error connecting NetworkFactory");
-                mNetworkFactoryInfos.remove(msg.obj);
+                mNetworkProviderInfos.remove(msg.obj);
             }
         } else if (mNetworkAgentInfos.containsKey(msg.replyTo)) {
             if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
@@ -3072,8 +3074,8 @@
         if (nai != null) {
             disconnectAndDestroyNetwork(nai);
         } else {
-            NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(msg.replyTo);
-            if (DBG && nfi != null) log("unregisterNetworkFactory for " + nfi.name);
+            NetworkProviderInfo npi = mNetworkProviderInfos.remove(msg.replyTo);
+            if (DBG && npi != null) log("unregisterNetworkFactory for " + npi.name);
         }
     }
 
@@ -3156,7 +3158,7 @@
             // ip[6]tables to flush routes and remove the incoming packet mark rule, so do it
             // after we've rematched networks with requests which should make a potential
             // fallback network the default or requested a new network from the
-            // NetworkFactories, so network traffic isn't interrupted for an unnecessarily
+            // NetworkProviders, so network traffic isn't interrupted for an unnecessarily
             // long time.
             destroyNativeNetwork(nai);
             mDnsManager.removeNetwork(nai.network);
@@ -3169,8 +3171,8 @@
             // This should never fail.  Specifying an already in use NetID will cause failure.
             if (networkAgent.isVPN()) {
                 mNetd.networkCreateVpn(networkAgent.network.netId,
-                        (networkAgent.networkMisc == null
-                                || !networkAgent.networkMisc.allowBypass));
+                        (networkAgent.networkAgentConfig == null
+                                || !networkAgent.networkAgentConfig.allowBypass));
             } else {
                 mNetd.networkCreatePhysical(networkAgent.network.netId,
                         getNetworkPermission(networkAgent.networkCapabilities));
@@ -3419,8 +3421,8 @@
                 }
             }
 
-            for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
-                nfi.cancelRequest(nri.request);
+            for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+                npi.cancelRequest(nri.request);
             }
         } else {
             // listens don't have a singular affectedNetwork.  Check all networks to see
@@ -3470,16 +3472,16 @@
             return;
         }
 
-        if (!nai.networkMisc.explicitlySelected) {
+        if (!nai.networkAgentConfig.explicitlySelected) {
             Slog.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network");
         }
 
-        if (accept != nai.networkMisc.acceptUnvalidated) {
-            nai.networkMisc.acceptUnvalidated = accept;
+        if (accept != nai.networkAgentConfig.acceptUnvalidated) {
+            nai.networkAgentConfig.acceptUnvalidated = accept;
             // If network becomes partial connectivity and user already accepted to use this
             // network, we should respect the user's option and don't need to popup the
             // PARTIAL_CONNECTIVITY notification to user again.
-            nai.networkMisc.acceptPartialConnectivity = accept;
+            nai.networkAgentConfig.acceptPartialConnectivity = accept;
             rematchAllNetworksAndRequests();
             sendUpdatedScoreToFactories(nai);
         }
@@ -3516,8 +3518,8 @@
             return;
         }
 
-        if (accept != nai.networkMisc.acceptPartialConnectivity) {
-            nai.networkMisc.acceptPartialConnectivity = accept;
+        if (accept != nai.networkAgentConfig.acceptPartialConnectivity) {
+            nai.networkAgentConfig.acceptPartialConnectivity = accept;
         }
 
         // TODO: Use the current design or save the user choice into IpMemoryStore.
@@ -3615,17 +3617,32 @@
                 enforceSettingsPermission();
             }
 
-            // getNetworkAgentInfoForNetwork is thread-safe
-            final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(mNetwork);
-            if (nai == null) return;
-
-            // nai.networkMonitor() is thread-safe
-            final NetworkMonitorManager nm = nai.networkMonitor();
+            final NetworkMonitorManager nm = getNetworkMonitorManager(mNetwork);
             if (nm == null) return;
             nm.notifyCaptivePortalAppFinished(response);
         }
 
         @Override
+        public void appRequest(final int request) {
+            final NetworkMonitorManager nm = getNetworkMonitorManager(mNetwork);
+            if (nm == null) return;
+
+            if (request == CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED) {
+                nm.forceReevaluation(Binder.getCallingUid());
+            }
+        }
+
+        @Nullable
+        private NetworkMonitorManager getNetworkMonitorManager(final Network network) {
+            // getNetworkAgentInfoForNetwork is thread-safe
+            final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+            if (nai == null) return null;
+
+            // nai.networkMonitor() is thread-safe
+            return nai.networkMonitor();
+        }
+
+        @Override
         public void logEvent(int eventId, String packageName) {
             enforceSettingsPermission();
 
@@ -3727,7 +3744,7 @@
                 action = ConnectivityManager.ACTION_PROMPT_PARTIAL_CONNECTIVITY;
                 // Don't bother the user with a high-priority notification if the network was not
                 // explicitly selected by the user.
-                highPriority = nai.networkMisc.explicitlySelected;
+                highPriority = nai.networkAgentConfig.explicitlySelected;
                 break;
             default:
                 Slog.wtf(TAG, "Unknown notification type " + type);
@@ -3760,14 +3777,15 @@
         // automatically connects to a network that has partial Internet access, the user will
         // always be able to use it, either because they've already chosen "don't ask again" or
         // because we have prompt them.
-        if (nai.partialConnectivity && !nai.networkMisc.acceptPartialConnectivity) {
+        if (nai.partialConnectivity && !nai.networkAgentConfig.acceptPartialConnectivity) {
             return true;
         }
 
         // If a network has no Internet access, only prompt if the network was explicitly selected
         // and if the user has not already told us to use the network regardless of whether it
         // validated or not.
-        if (nai.networkMisc.explicitlySelected && !nai.networkMisc.acceptUnvalidated) {
+        if (nai.networkAgentConfig.explicitlySelected
+                && !nai.networkAgentConfig.acceptUnvalidated) {
             return true;
         }
 
@@ -3855,12 +3873,12 @@
                     handleApplyDefaultProxy((ProxyInfo)msg.obj);
                     break;
                 }
-                case EVENT_REGISTER_NETWORK_FACTORY: {
-                    handleRegisterNetworkFactory((NetworkFactoryInfo)msg.obj);
+                case EVENT_REGISTER_NETWORK_PROVIDER: {
+                    handleRegisterNetworkProvider((NetworkProviderInfo) msg.obj);
                     break;
                 }
-                case EVENT_UNREGISTER_NETWORK_FACTORY: {
-                    handleUnregisterNetworkFactory((Messenger)msg.obj);
+                case EVENT_UNREGISTER_NETWORK_PROVIDER: {
+                    handleUnregisterNetworkProvider((Messenger) msg.obj);
                     break;
                 }
                 case EVENT_REGISTER_NETWORK_AGENT: {
@@ -4905,7 +4923,7 @@
         }
     };
 
-    private final HashMap<Messenger, NetworkFactoryInfo> mNetworkFactoryInfos = new HashMap<>();
+    private final HashMap<Messenger, NetworkProviderInfo> mNetworkProviderInfos = new HashMap<>();
     private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = new HashMap<>();
 
     private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
@@ -4913,18 +4931,18 @@
     @GuardedBy("mUidToNetworkRequestCount")
     private final SparseIntArray mUidToNetworkRequestCount = new SparseIntArray();
 
-    private static class NetworkFactoryInfo {
+    private static class NetworkProviderInfo {
         public final String name;
         public final Messenger messenger;
         private final AsyncChannel mAsyncChannel;
         private final IBinder.DeathRecipient mDeathRecipient;
-        public final int factorySerialNumber;
+        public final int providerId;
 
-        NetworkFactoryInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
-                int factorySerialNumber, IBinder.DeathRecipient deathRecipient) {
+        NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
+                int providerId, IBinder.DeathRecipient deathRecipient) {
             this.name = name;
             this.messenger = messenger;
-            this.factorySerialNumber = factorySerialNumber;
+            this.providerId = providerId;
             mAsyncChannel = asyncChannel;
             mDeathRecipient = deathRecipient;
 
@@ -4942,17 +4960,17 @@
                 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.
+                // NetworkProviderInfo from mNetworkProviderInfos.
             }
         }
 
-        void requestNetwork(NetworkRequest request, int score, int servingSerialNumber) {
+        void requestNetwork(NetworkRequest request, int score, int servingProviderId) {
             if (isLegacyNetworkFactory()) {
                 mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
-                        servingSerialNumber, request);
+                        servingProviderId, request);
             } else {
                 sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
-                            servingSerialNumber, request);
+                            servingProviderId, request);
             }
         }
 
@@ -5365,45 +5383,45 @@
     @Override
     public int registerNetworkFactory(Messenger messenger, String name) {
         enforceNetworkFactoryPermission();
-        NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel(),
+        NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, new AsyncChannel(),
                 nextNetworkProviderId(), null /* deathRecipient */);
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));
-        return nfi.factorySerialNumber;
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
+        return npi.providerId;
     }
 
-    private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) {
-        if (mNetworkFactoryInfos.containsKey(nfi.messenger)) {
+    private void handleRegisterNetworkProvider(NetworkProviderInfo npi) {
+        if (mNetworkProviderInfos.containsKey(npi.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);
+            Slog.e(TAG, "Attempt to register existing NetworkProviderInfo "
+                    + mNetworkProviderInfos.get(npi.messenger).name);
             return;
         }
 
-        if (DBG) log("Got NetworkFactory Messenger for " + nfi.name);
-        mNetworkFactoryInfos.put(nfi.messenger, nfi);
-        nfi.connect(mContext, mTrackerHandler);
-        if (!nfi.isLegacyNetworkFactory()) {
+        if (DBG) log("Got NetworkProvider Messenger for " + npi.name);
+        mNetworkProviderInfos.put(npi.messenger, npi);
+        npi.connect(mContext, mTrackerHandler);
+        if (!npi.isLegacyNetworkFactory()) {
             // Legacy NetworkFactories get their requests when their AsyncChannel connects.
-            sendAllRequestsToFactory(nfi);
+            sendAllRequestsToProvider(npi);
         }
     }
 
     @Override
     public int registerNetworkProvider(Messenger messenger, String name) {
         enforceNetworkFactoryPermission();
-        NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger,
+        NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger,
                 null /* asyncChannel */, nextNetworkProviderId(),
                 () -> unregisterNetworkProvider(messenger));
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));
-        return nfi.factorySerialNumber;
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
+        return npi.providerId;
     }
 
     @Override
     public void unregisterNetworkProvider(Messenger messenger) {
         enforceNetworkFactoryPermission();
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_FACTORY, messenger));
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger));
     }
 
     @Override
@@ -5411,13 +5429,13 @@
         unregisterNetworkProvider(messenger);
     }
 
-    private void handleUnregisterNetworkFactory(Messenger messenger) {
-        NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(messenger);
-        if (nfi == null) {
-            loge("Failed to find Messenger in unregisterNetworkFactory");
+    private void handleUnregisterNetworkProvider(Messenger messenger) {
+        NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger);
+        if (npi == null) {
+            loge("Failed to find Messenger in unregisterNetworkProvider");
             return;
         }
-        if (DBG) log("unregisterNetworkFactory for " + nfi.name);
+        if (DBG) log("unregisterNetworkProvider for " + npi.name);
     }
 
     @Override
@@ -5489,9 +5507,9 @@
     // tree.
     public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkMisc networkMisc) {
+            int currentScore, NetworkAgentConfig networkAgentConfig) {
         return registerNetworkAgent(messenger, networkInfo, linkProperties, networkCapabilities,
-                currentScore, networkMisc, NetworkFactory.SerialNumber.NONE);
+                currentScore, networkAgentConfig, NetworkProvider.ID_NONE);
     }
 
     /**
@@ -5506,12 +5524,12 @@
      *         later : see {@link #updateCapabilities}.
      * @param currentScore the initial score of the network. See
      *         {@link NetworkAgentInfo#getCurrentScore}.
-     * @param networkMisc metadata about the network. This is never updated.
-     * @param factorySerialNumber the serial number of the factory owning this NetworkAgent.
+     * @param networkAgentConfig metadata about the network. This is never updated.
+     * @param providerId the ID of the provider owning this NetworkAgent.
      */
     public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkMisc networkMisc, int factorySerialNumber) {
+            int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
         enforceNetworkFactoryPermission();
 
         LinkProperties lp = new LinkProperties(linkProperties);
@@ -5523,8 +5541,8 @@
         ns.putIntExtension(NetworkScore.LEGACY_SCORE, currentScore);
         final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
                 new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
-                ns, mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd,
-                mDnsResolver, mNMS, factorySerialNumber);
+                ns, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig), this,
+                mNetd, mDnsResolver, mNMS, providerId);
         // Make sure the network capabilities reflect what the agent info says.
         nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
         final String extraInfo = networkInfo.getExtraInfo();
@@ -5803,7 +5821,7 @@
         // Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
          // Don't complain for VPNs since they're not driven by requests and there is no risk of
          // causing a connect/teardown loop.
-         // TODO: remove this altogether and make it the responsibility of the NetworkFactories to
+         // TODO: remove this altogether and make it the responsibility of the NetworkProviders to
          // avoid connect/teardown loops.
         if (nai.everConnected &&
                 !nai.isVPN() &&
@@ -5945,7 +5963,7 @@
             LinkProperties lp) {
         if (nc == null || lp == null) return false;
         return nai.isVPN()
-                && !nai.networkMisc.allowBypass
+                && !nai.networkAgentConfig.allowBypass
                 && nc.getEstablishingVpnAppUid() != Process.SYSTEM_UID
                 && lp.getInterfaceName() != null
                 && (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute());
@@ -6043,13 +6061,13 @@
         if (VDBG || DDBG){
             log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
         }
-        for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
-            nfi.requestNetwork(networkRequest, score, serial);
+        for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+            npi.requestNetwork(networkRequest, score, serial);
         }
     }
 
     /** Sends all current NetworkRequests to the specified factory. */
-    private void sendAllRequestsToFactory(NetworkFactoryInfo nfi) {
+    private void sendAllRequestsToProvider(NetworkProviderInfo npi) {
         ensureRunningOnConnectivityServiceThread();
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
             if (nri.request.isListen()) continue;
@@ -6061,9 +6079,9 @@
                 serial = nai.factorySerialNumber;
             } else {
                 score = 0;
-                serial = NetworkFactory.SerialNumber.NONE;
+                serial = NetworkProvider.ID_NONE;
             }
-            nfi.requestNetwork(nri.request, score, serial);
+            npi.requestNetwork(nri.request, score, serial);
         }
     }
 
@@ -6344,11 +6362,11 @@
                     Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
                 }
                 addedRequests.add(nri);
-                // Tell NetworkFactories about the new score, so they can stop
+                // Tell NetworkProviders about the new score, so they can stop
                 // trying to connect if they know they cannot match it.
                 // TODO - this could get expensive if we have a lot of requests for this
                 // network.  Think about if there is a way to reduce this.  Push
-                // netid->request mapping to each factory?
+                // netid->request mapping to each provider?
                 sendUpdatedScoreToFactories(nri.request, newSatisfier);
                 if (isDefaultRequest(nri)) {
                     isNewDefault = true;
@@ -6377,7 +6395,7 @@
                 } else {
                     Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
                             newNetwork.name() +
-                            " without updating mSatisfier or factories!");
+                            " without updating mSatisfier or providers!");
                 }
                 // TODO: Technically, sending CALLBACK_LOST here is
                 // incorrect if there is a replacement network currently
@@ -6636,7 +6654,7 @@
             // command must be sent after updating LinkProperties to maximize chances of
             // NetworkMonitor seeing the correct LinkProperties when starting.
             // TODO: pass LinkProperties to the NetworkMonitor in the notifyNetworkConnected call.
-            if (networkAgent.networkMisc.acceptPartialConnectivity) {
+            if (networkAgent.networkAgentConfig.acceptPartialConnectivity) {
                 networkAgent.networkMonitor().setAcceptPartialConnectivity();
             }
             networkAgent.networkMonitor().notifyNetworkConnected(
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
index d20936c..7894788 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
@@ -154,17 +154,20 @@
 
     private void onPollNetworkTimeUnderWakeLock(int event) {
         // Force an NTP fix when outdated
-        if (mTime.getCacheAge() >= mPollingIntervalMs) {
+        NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
+        if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
             if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
             mTime.forceRefresh();
+            cachedNtpResult = mTime.getCachedTimeResult();
         }
 
-        if (mTime.getCacheAge() < mPollingIntervalMs) {
+        if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
             // Obtained fresh fix; schedule next normal update
             resetAlarm(mPollingIntervalMs);
 
             // Suggest the time to the time detector. It may choose use it to set the system clock.
-            TimestampedValue<Long> timeSignal = mTime.getCachedNtpTimeSignal();
+            TimestampedValue<Long> timeSignal = new TimestampedValue<>(
+                    cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis());
             NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
             timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateServiceImpl. event=" + event);
             mTimeDetector.suggestNetworkTime(timeSuggestion);
@@ -275,8 +278,11 @@
         TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
         pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
         pw.println("\nTryAgainCounter: " + mTryAgainCounter);
-        pw.println("NTP cache age: " + mTime.getCacheAge());
-        pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
+        NtpTrustedTime.TimeResult ntpResult = mTime.getCachedTimeResult();
+        pw.println("NTP cache result: " + ntpResult);
+        if (ntpResult != null) {
+            pw.println("NTP result age: " + ntpResult.getAgeMillis());
+        }
         pw.println();
     }
 }
diff --git a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
index 190fff1..21fa9f9 100644
--- a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
+++ b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
@@ -46,4 +46,7 @@
 
     /** Update the OEM unlock enabled bit, bypassing user restriction checks. */
     void forceOemUnlockEnabled(boolean enabled);
+
+    /** Retrieves the UID that can access the persistent data partition. */
+    int getAllowedUid();
 }
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 73c8520..00d8b0f 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -680,6 +680,11 @@
             writeDataBuffer(getTestHarnessModeDataOffset(), ByteBuffer.allocate(size));
         }
 
+        @Override
+        public int getAllowedUid() {
+            return mAllowedUid;
+        }
+
         private void writeInternal(byte[] data, long offset, int dataLength) {
             checkArgument(data == null || data.length > 0, "data must be null or non-empty");
             checkArgument(
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index c5409f85..f46b9ae 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -18,18 +18,23 @@
 
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityThread;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.os.IBinder;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.os.UserManager;
 
 import com.android.server.pm.UserManagerService;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -57,15 +62,17 @@
  *
  * {@hide}
  */
+//@SystemApi(client = Client.MODULE_LIBRARIES, process = Process.SYSTEM_SERVER)
 public abstract class SystemService {
 
+    /** @hide */
     // TODO(b/133242016) STOPSHIP: change to false before R ships
     protected static final boolean DEBUG_USER = true;
 
     /*
-     * Boot Phases
+     * The earliest boot phase the system send to system services on boot.
      */
-    public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100; // maybe should be a dependency?
+    public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100;
 
     /**
      * After receiving this boot phase, services can obtain lock settings data.
@@ -98,13 +105,70 @@
      * After receiving this boot phase, services can allow user interaction with the device.
      * This phase occurs when boot has completed and the home application has started.
      * System services may prefer to listen to this phase rather than registering a
-     * broadcast receiver for ACTION_BOOT_COMPLETED to reduce overall latency.
+     * broadcast receiver for {@link android.content.Intent#ACTION_LOCKED_BOOT_COMPLETED}
+     * to reduce overall latency.
      */
     public static final int PHASE_BOOT_COMPLETED = 1000;
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "PHASE_" }, value = {
+            PHASE_WAIT_FOR_DEFAULT_DISPLAY,
+            PHASE_LOCK_SETTINGS_READY,
+            PHASE_SYSTEM_SERVICES_READY,
+            PHASE_DEVICE_SPECIFIC_SERVICES_READY,
+            PHASE_ACTIVITY_MANAGER_READY,
+            PHASE_THIRD_PARTY_APPS_CAN_START,
+            PHASE_BOOT_COMPLETED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BootPhase {}
+
     private final Context mContext;
 
     /**
+     * Class representing user in question in the lifecycle callbacks.
+     * @hide
+     */
+    //@SystemApi(client = Client.MODULE_LIBRARIES, process = Process.SYSTEM_SERVER)
+    public static final class TargetUser {
+        @NonNull
+        private final UserInfo mUserInfo;
+
+        /** @hide */
+        public TargetUser(@NonNull UserInfo userInfo) {
+            mUserInfo = userInfo;
+        }
+
+        /**
+         * @return The information about the user. <b>NOTE: </b> this is a "live" object
+         * referenced by {@link UserManagerService} and hence should not be modified.
+         *
+         * @hide
+         */
+        @NonNull
+        public UserInfo getUserInfo() {
+            return mUserInfo;
+        }
+
+        /**
+         * @return the target {@link UserHandle}.
+         */
+        @NonNull
+        public UserHandle getUserHandle() {
+            return mUserInfo.getUserHandle();
+        }
+
+        /**
+         * @return the integer user id
+         *
+         * @hide
+         */
+        public int getUserIdentifier() {
+            return mUserInfo.id;
+        }
+    }
+
+    /**
      * Initializes the system service.
      * <p>
      * Subclasses must define a single argument constructor that accepts the context
@@ -113,13 +177,14 @@
      *
      * @param context The system server context.
      */
-    public SystemService(Context context) {
+    public SystemService(@NonNull Context context) {
         mContext = context;
     }
 
     /**
      * Gets the system context.
      */
+    @NonNull
     public final Context getContext() {
         return mContext;
     }
@@ -128,6 +193,8 @@
      * Get the system UI context. This context is to be used for displaying UI. It is themable,
      * which means resources can be overridden at runtime. Do not use to retrieve properties that
      * configure the behavior of the device that is not UX related.
+     *
+     * @hide
      */
     public final Context getUiContext() {
         // This has already been set up by the time any SystemServices are created.
@@ -137,15 +204,16 @@
     /**
      * Returns true if the system is running in safe mode.
      * TODO: we should define in which phase this becomes valid
+     *
+     * @hide
      */
     public final boolean isSafeMode() {
         return getManager().isSafeMode();
     }
 
     /**
-     * Called when the dependencies listed in the @Service class-annotation are available
-     * and after the chosen start phase.
-     * When this method returns, the service should be published.
+     * Called when the system service should publish a binder service using
+     * {@link #publishBinderService(String, IBinder).}
      */
     public abstract void onStart();
 
@@ -155,7 +223,7 @@
      *
      * @param phase The current boot phase.
      */
-    public void onBootPhase(int phase) {}
+    public void onBootPhase(@BootPhase int phase) {}
 
     /**
      * Checks if the service should be available for the given user.
@@ -163,12 +231,14 @@
      * <p>By default returns {@code true}, but subclasses should extend for optimization, if they
      * don't support some types (like headless system user).
      */
-    public boolean isSupported(@NonNull UserInfo userInfo) {
+    public boolean isSupportedUser(@NonNull TargetUser user) {
         return true;
     }
 
     /**
-     * Helper method used to dump which users are {@link #onStartUser(UserInfo) supported}.
+     * Helper method used to dump which users are {@link #onStartUser(TargetUser) supported}.
+     *
+     * @hide
      */
     protected void dumpSupportedUsers(@NonNull PrintWriter pw, @NonNull String prefix) {
         final List<UserInfo> allUsers = UserManager.get(mContext).getUsers();
@@ -187,34 +257,59 @@
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onStartUser(UserInfo)} instead (which by default
-     * calls this method).
+     * @deprecated subclasses should extend {@link #onStartUser(TargetUser)} instead
+     * (which by default calls this method).
+     *
+     * @hide
      */
     @Deprecated
     public void onStartUser(@UserIdInt int userId) {}
 
     /**
-     * Called when a new user is starting, for system services to initialize any per-user
-     * state they maintain for running users.
+     * @deprecated subclasses should extend {@link #onStartUser(TargetUser)} instead
+     * (which by default calls this method).
      *
-     * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this
-     * user.
-     *
-     * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
-     * referenced by {@link UserManagerService} and hence should not be modified.
+     * @hide
      */
+    @Deprecated
     public void onStartUser(@NonNull UserInfo userInfo) {
         onStartUser(userInfo.id);
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onUnlockUser(UserInfo)} instead (which by
+     * Called when a new user is starting, for system services to initialize any per-user
+     * state they maintain for running users.
+     *
+     * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * this user.
+     *
+     * @param user target user
+     */
+    public void onStartUser(@NonNull TargetUser user) {
+        onStartUser(user.getUserInfo());
+    }
+
+    /**
+     * @deprecated subclasses should extend {@link #onUnlockUser(TargetUser)} instead (which by
      * default calls this method).
+     *
+     * @hide
      */
     @Deprecated
     public void onUnlockUser(@UserIdInt int userId) {}
 
     /**
+     * @deprecated subclasses should extend {@link #onUnlockUser(TargetUser)} instead (which by
+     * default calls this method).
+     *
+     * @hide
+     */
+    @Deprecated
+    public void onUnlockUser(@NonNull UserInfo userInfo) {
+        onUnlockUser(userInfo.id);
+    }
+
+    /**
      * Called when an existing user is in the process of being unlocked. This
      * means the credential-encrypted storage for that user is now available,
      * and encryption-aware component filtering is no longer in effect.
@@ -226,90 +321,127 @@
      * {@link UserManager#isUserUnlockingOrUnlocked(int)} to handle both of
      * these states.
      * <p>
-     * This method is only called when the service {@link #isSupported(UserInfo) supports} this
-     * user.
+     * This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * this user.
      *
-     * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
-     * referenced by {@link UserManagerService} and hence should not be modified.
+     * @param user target user
      */
-    public void onUnlockUser(@NonNull UserInfo userInfo) {
-        onUnlockUser(userInfo.id);
+    public void onUnlockUser(@NonNull TargetUser user) {
+        onUnlockUser(user.getUserInfo());
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onSwitchUser(UserInfo, UserInfo)} instead
+     * @deprecated subclasses should extend {@link #onSwitchUser(TargetUser, TargetUser)} instead
      * (which by default calls this method).
+     *
+     * @hide
      */
     @Deprecated
-    public void onSwitchUser(@UserIdInt int userId) {}
+    public void onSwitchUser(@UserIdInt int toUserId) {}
+
+    /**
+     * @deprecated subclasses should extend {@link #onSwitchUser(TargetUser, TargetUser)} instead
+     * (which by default calls this method).
+     *
+     * @hide
+     */
+    @Deprecated
+    public void onSwitchUser(@Nullable UserInfo from, @NonNull UserInfo to) {
+        onSwitchUser(to.id);
+    }
 
     /**
      * Called when switching to a different foreground user, for system services that have
      * special behavior for whichever user is currently in the foreground.  This is called
      * before any application processes are aware of the new user.
      *
-     * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} either
-     * of the users ({@code from} or {@code to}).
+     * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * either of the users ({@code from} or {@code to}).
      *
      * <b>NOTE: </b> both {@code from} and {@code to} are "live" objects
      * referenced by {@link UserManagerService} and hence should not be modified.
      *
-     * @param from The information about the user being switched from.
-     * @param to The information about the user being switched from to.
+     * @param from the user switching from
+     * @param to the user switching to
      */
-    public void onSwitchUser(@NonNull UserInfo from, @NonNull UserInfo to) {
-        onSwitchUser(to.id);
+    public void onSwitchUser(@Nullable TargetUser from, @NonNull TargetUser to) {
+        onSwitchUser((from == null ? null : from.getUserInfo()), to.getUserInfo());
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onStopUser(UserInfo)} instead (which by default
-     * calls this method).
+     * @deprecated subclasses should extend {@link #onStopUser(TargetUser)} instead
+     * (which by default calls this method).
+     *
+     * @hide
      */
     @Deprecated
     public void onStopUser(@UserIdInt int userId) {}
 
     /**
+     * @deprecated subclasses should extend {@link #onStopUser(TargetUser)} instead
+     * (which by default calls this method).
+     *
+     * @hide
+     */
+    @Deprecated
+    public void onStopUser(@NonNull UserInfo user) {
+        onStopUser(user.id);
+
+    }
+
+    /**
      * Called when an existing user is stopping, for system services to finalize any per-user
      * state they maintain for running users.  This is called prior to sending the SHUTDOWN
      * broadcast to the user; it is a good place to stop making use of any resources of that
      * user (such as binding to a service running in the user).
      *
-     * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this
-     * user.
+     * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * this user.
      *
      * <p>NOTE: This is the last callback where the callee may access the target user's CE storage.
      *
-     * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
-     * referenced by {@link UserManagerService} and hence should not be modified.
+     * @param user target user
      */
-    public void onStopUser(@NonNull UserInfo userInfo) {
-        onStopUser(userInfo.id);
+    public void onStopUser(@NonNull TargetUser user) {
+        onStopUser(user.getUserInfo());
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onCleanupUser(UserInfo)} instead (which by
+     * @deprecated subclasses should extend {@link #onCleanupUser(TargetUser)} instead (which by
      * default calls this method).
+     *
+     * @hide
      */
     @Deprecated
     public void onCleanupUser(@UserIdInt int userId) {}
 
     /**
+     * @deprecated subclasses should extend {@link #onCleanupUser(TargetUser)} instead (which by
+     * default calls this method).
+     *
+     * @hide
+     */
+    @Deprecated
+    public void onCleanupUser(@NonNull UserInfo user) {
+        onCleanupUser(user.id);
+    }
+
+    /**
      * Called when an existing user is stopping, for system services to finalize any per-user
      * state they maintain for running users.  This is called after all application process
      * teardown of the user is complete.
      *
-     * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this
-     * user.
+     * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * this user.
      *
      * <p>NOTE: When this callback is called, the CE storage for the target user may not be
-     * accessible already.  Use {@link #onStopUser(UserInfo)} instead if you need to access the CE
+     * accessible already.  Use {@link #onStopUser(TargetUser)} instead if you need to access the CE
      * storage.
      *
-     * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
-     * referenced by {@link UserManagerService} and hence should not be modified.
+     * @param user target user
      */
-    public void onCleanupUser(@NonNull UserInfo userInfo) {
-        onCleanupUser(userInfo.id);
+    public void onCleanupUser(@NonNull TargetUser user) {
+        onCleanupUser(user.getUserInfo());
     }
 
     /**
@@ -318,7 +450,7 @@
      * @param name the name of the new service
      * @param service the service object
      */
-    protected final void publishBinderService(String name, IBinder service) {
+    protected final void publishBinderService(@NonNull String name, @NonNull IBinder service) {
         publishBinderService(name, service, false);
     }
 
@@ -330,7 +462,7 @@
      * @param allowIsolated set to true to allow isolated sandboxed processes
      * to access this service
      */
-    protected final void publishBinderService(String name, IBinder service,
+    protected final void publishBinderService(@NonNull String name, @NonNull IBinder service,
             boolean allowIsolated) {
         publishBinderService(name, service, allowIsolated, DUMP_FLAG_PRIORITY_DEFAULT);
     }
@@ -343,6 +475,8 @@
      * @param allowIsolated set to true to allow isolated sandboxed processes
      * to access this service
      * @param dumpPriority supported dump priority levels as a bitmask
+     *
+     * @hide
      */
     protected final void publishBinderService(String name, IBinder service,
             boolean allowIsolated, int dumpPriority) {
@@ -351,6 +485,8 @@
 
     /**
      * Get a binder service by its name.
+     *
+     * @hide
      */
     protected final IBinder getBinderService(String name) {
         return ServiceManager.getService(name);
@@ -358,6 +494,8 @@
 
     /**
      * Publish the service so it is only accessible to the system process.
+     *
+     * @hide
      */
     protected final <T> void publishLocalService(Class<T> type, T service) {
         LocalServices.addService(type, service);
@@ -365,6 +503,8 @@
 
     /**
      * Get a local service by interface.
+     *
+     * @hide
      */
     protected final <T> T getLocalService(Class<T> type) {
         return LocalServices.getService(type);
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index c715798..c0f43a8 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -27,6 +27,7 @@
 import android.os.UserManagerInternal;
 import android.util.Slog;
 
+import com.android.server.SystemService.TargetUser;
 import com.android.server.utils.TimingsTraceAndSlog;
 
 import java.io.File;
@@ -264,26 +265,26 @@
             @UserIdInt int curUserId, @UserIdInt int prevUserId) {
         t.traceBegin("ssm." + onWhat + "User-" + curUserId);
         Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId);
-        final UserInfo curUserInfo = getUserInfo(curUserId);
-        final UserInfo prevUserInfo = prevUserId == UserHandle.USER_NULL ? null
-                : getUserInfo(prevUserId);
+        final TargetUser curUser = new TargetUser(getUserInfo(curUserId));
+        final TargetUser prevUser = prevUserId == UserHandle.USER_NULL ? null
+                : new TargetUser(getUserInfo(prevUserId));
         final int serviceLen = mServices.size();
         for (int i = 0; i < serviceLen; i++) {
             final SystemService service = mServices.get(i);
             final String serviceName = service.getClass().getName();
-            boolean supported = service.isSupported(curUserInfo);
+            boolean supported = service.isSupportedUser(curUser);
 
             // Must check if either curUser or prevUser is supported (for example, if switching from
             // unsupported to supported, we still need to notify the services)
-            if (!supported && prevUserInfo != null) {
-                supported = service.isSupported(prevUserInfo);
+            if (!supported && prevUser != null) {
+                supported = service.isSupportedUser(prevUser);
             }
 
             if (!supported) {
                 if (DEBUG) {
                     Slog.d(TAG, "Skipping " + onWhat + "User-" + curUserId + " on service "
                             + serviceName + " because it's not supported (curUser: "
-                            + curUserInfo + ", prevUser:" + prevUserInfo + ")");
+                            + curUser + ", prevUser:" + prevUser + ")");
                 } else {
                     Slog.i(TAG,  "Skipping " + onWhat + "User-" + curUserId + " on "
                             + serviceName);
@@ -295,25 +296,25 @@
             try {
                 switch (onWhat) {
                     case SWITCH:
-                        service.onSwitchUser(prevUserInfo, curUserInfo);
+                        service.onSwitchUser(prevUser, curUser);
                         break;
                     case START:
-                        service.onStartUser(curUserInfo);
+                        service.onStartUser(curUser);
                         break;
                     case UNLOCK:
-                        service.onUnlockUser(curUserInfo);
+                        service.onUnlockUser(curUser);
                         break;
                     case STOP:
-                        service.onStopUser(curUserInfo);
+                        service.onStopUser(curUser);
                         break;
                     case CLEANUP:
-                        service.onCleanupUser(curUserInfo);
+                        service.onCleanupUser(curUser);
                         break;
                     default:
                         throw new IllegalArgumentException(onWhat + " what?");
                 }
             } catch (Exception ex) {
-                Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + curUserInfo
+                Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + curUser
                         + " to service " + serviceName, ex);
             }
             warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
diff --git a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java
index ebfc2a0..549051d 100644
--- a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java
@@ -32,6 +32,7 @@
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.UserManager;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -81,8 +82,15 @@
                     .setImageDrawable(drawable);
         }
 
-        ((TextView) view.findViewById(R.id.user_loading))
-                .setText(res.getString(R.string.car_loading_profile));
+        TextView msgView = view.findViewById(R.id.user_loading);
+        // TODO: use developer settings instead
+        if (Build.IS_DEBUGGABLE) {
+            // TODO: use specific string
+            msgView.setText(res.getString(R.string.car_loading_profile) + " user\n(from "
+                    + mOldUser.id + " to " + mNewUser.id + ")");
+        } else {
+            msgView.setText(res.getString(R.string.car_loading_profile));
+        }
         setView(view);
     }
 
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b9f2e76..e11008c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1562,8 +1562,7 @@
                     throw e.rethrowAsRuntimeException();
                 }
                 int numGids = 3;
-                if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER
-                        || mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
+                if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
                     numGids++;
                 }
                 /*
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 2b20782..8ae18ff6 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1645,10 +1645,9 @@
             final boolean allow;
             final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, targetUserId);
             if (mInjector.isCallerRecents(callingUid)
-                    && callingUserId == getCurrentUserId()
-                    && isSameProfileGroup) {
-                // If the caller is Recents and it is running in the current user, we then allow it
-                // to access its profiles.
+                    && isSameProfileGroup(callingUserId, targetUserId)) {
+                // If the caller is Recents and the caller has ownership of the profile group,
+                // we then allow it to access its profiles.
                 allow = true;
             } else if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
                     callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index a7593c7..e3b761e 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1759,8 +1759,9 @@
                 ? opNames.toArray(new String[opNames.size()]) : null;
 
         // Must not hold the appops lock
-        mHistoricalRegistry.getHistoricalOps(uid, packageName, featureId, opNamesArray, filter,
-                beginTimeMillis, endTimeMillis, flags, callback);
+        mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps,
+                mHistoricalRegistry, uid, packageName, featureId, opNamesArray, filter,
+                beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
     }
 
     @Override
@@ -1778,8 +1779,9 @@
                 ? opNames.toArray(new String[opNames.size()]) : null;
 
         // Must not hold the appops lock
-        mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, featureId, opNamesArray,
-                filter, beginTimeMillis, endTimeMillis, flags, callback);
+        mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw,
+                mHistoricalRegistry, uid, packageName, featureId, opNamesArray,
+                filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bc4dc8f..cd2272a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1700,6 +1700,14 @@
         }
     }
 
+    /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
+    public @NonNull ArrayList<AudioDeviceAddress> getDevicesForAttributes(
+            @NonNull AudioAttributes attributes) {
+        Objects.requireNonNull(attributes);
+        enforceModifyAudioRoutingPermission();
+        return AudioSystem.getDevicesForAttributes(attributes);
+    }
+
 
     /** @see AudioManager#adjustVolume(int, int) */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index aea6d8d..f636d67 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -116,7 +116,8 @@
                 && !lp.hasIpv4Address();
 
         // If the network tells us it doesn't use clat, respect that.
-        final boolean skip464xlat = (nai.netMisc() != null) && nai.netMisc().skip464xlat;
+        final boolean skip464xlat = (nai.netAgentConfig() != null)
+                && nai.netAgentConfig().skip464xlat;
 
         return supported && connected && isIpv6OnlyNetwork && !skip464xlat;
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 5e085ca..c1ab551 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -23,9 +23,9 @@
 import android.net.INetworkMonitor;
 import android.net.LinkProperties;
 import android.net.Network;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
-import android.net.NetworkMisc;
 import android.net.NetworkMonitorManager;
 import android.net.NetworkRequest;
 import android.net.NetworkScore;
@@ -127,7 +127,7 @@
     // This should only be modified by ConnectivityService, via setNetworkCapabilities().
     // TODO: make this private with a getter.
     public NetworkCapabilities networkCapabilities;
-    public final NetworkMisc networkMisc;
+    public final NetworkAgentConfig networkAgentConfig;
     // Indicates if netd has been told to create this Network. From this point on the appropriate
     // routing rules are setup and routes are added so packets can begin flowing over the Network.
     // This is a sticky bit; once set it is never cleared.
@@ -261,7 +261,7 @@
 
     public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
             LinkProperties lp, NetworkCapabilities nc, @NonNull NetworkScore ns, Context context,
-            Handler handler, NetworkMisc misc, ConnectivityService connService, INetd netd,
+            Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
             IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber) {
         this.messenger = messenger;
         asyncChannel = ac;
@@ -274,7 +274,7 @@
         mConnService = connService;
         mContext = context;
         mHandler = handler;
-        networkMisc = misc;
+        networkAgentConfig = config;
         this.factorySerialNumber = factorySerialNumber;
     }
 
@@ -309,8 +309,8 @@
         return mConnService;
     }
 
-    public NetworkMisc netMisc() {
-        return networkMisc;
+    public NetworkAgentConfig netAgentConfig() {
+        return networkAgentConfig;
     }
 
     public Handler handler() {
@@ -487,7 +487,8 @@
         // selected and we're trying to see what its score could be. This ensures that we don't tear
         // down an explicitly selected network before the user gets a chance to prefer it when
         // a higher-scoring network (e.g., Ethernet) is available.
-        if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
+        if (networkAgentConfig.explicitlySelected
+                && (networkAgentConfig.acceptUnvalidated || pretendValidated)) {
             return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
         }
 
@@ -533,7 +534,8 @@
         synchronized (this) {
             // Network objects are outwardly immutable so there is no point in duplicating.
             // Duplicating also precludes sharing socket factories and connection pools.
-            final String subscriberId = (networkMisc != null) ? networkMisc.subscriberId : null;
+            final String subscriberId = (networkAgentConfig != null)
+                    ? networkAgentConfig.subscriberId : null;
             return new NetworkState(new NetworkInfo(networkInfo),
                     new LinkProperties(linkProperties),
                     new NetworkCapabilities(networkCapabilities), network, subscriberId, null);
@@ -641,13 +643,13 @@
                 + "nc{" + networkCapabilities + "}  Score{" + getCurrentScore() + "}  "
                 + "everValidated{" + everValidated + "}  lastValidated{" + lastValidated + "}  "
                 + "created{" + created + "} lingering{" + isLingering() + "} "
-                + "explicitlySelected{" + networkMisc.explicitlySelected + "} "
-                + "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} "
+                + "explicitlySelected{" + networkAgentConfig.explicitlySelected + "} "
+                + "acceptUnvalidated{" + networkAgentConfig.acceptUnvalidated + "} "
                 + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} "
                 + "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} "
                 + "captivePortalValidationPending{" + captivePortalValidationPending + "} "
                 + "partialConnectivity{" + partialConnectivity + "} "
-                + "acceptPartialConnectivity{" + networkMisc.acceptPartialConnectivity + "} "
+                + "acceptPartialConnectivity{" + networkAgentConfig.acceptPartialConnectivity + "} "
                 + "clat{" + clatd + "} "
                 + "}";
     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6b70e5f..476f3f8 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -53,11 +53,11 @@
 import android.net.LocalSocketAddress;
 import android.net.Network;
 import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
-import android.net.NetworkFactory;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkMisc;
+import android.net.NetworkProvider;
 import android.net.RouteInfo;
 import android.net.UidRange;
 import android.net.VpnService;
@@ -914,7 +914,7 @@
      * has certain changes, in which case this method would just return {@code false}.
      */
     private boolean updateLinkPropertiesInPlaceIfPossible(NetworkAgent agent, VpnConfig oldConfig) {
-        // NetworkMisc cannot be updated without registering a new NetworkAgent.
+        // NetworkAgentConfig cannot be updated without registering a new NetworkAgent.
         if (oldConfig.allowBypass != mConfig.allowBypass) {
             Log.i(TAG, "Handover not possible due to changes to allowBypass");
             return false;
@@ -947,8 +947,8 @@
 
         mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null);
 
-        NetworkMisc networkMisc = new NetworkMisc();
-        networkMisc.allowBypass = mConfig.allowBypass && !mLockdown;
+        NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
+        networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown;
 
         mNetworkCapabilities.setEstablishingVpnAppUid(Binder.getCallingUid());
         mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserHandle,
@@ -957,8 +957,8 @@
         try {
             mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */,
                     mNetworkInfo, mNetworkCapabilities, lp,
-                    ConnectivityConstants.VPN_DEFAULT_SCORE, networkMisc,
-                    NetworkFactory.SerialNumber.VPN) {
+                    ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig,
+                    NetworkProvider.ID_VPN) {
                             @Override
                             public void unwanted() {
                                 // We are user controlled, not driven by NetworkRequest.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d7ae5b5..e39d6d5 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1305,21 +1305,13 @@
     }
 
     private SurfaceControl.ScreenshotGraphicBuffer screenshotInternal(int displayId) {
-        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 */);
+        final IBinder token = getDisplayToken(displayId);
+        if (token == null) {
+            return null;
         }
+        return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(
+                        token, new Rect(), 0 /* width */, 0 /* height */,
+                        false /* useIdentityTransform */, 0 /* rotation */);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index c58000f..8206fef 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -41,7 +41,6 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.infra.AbstractRemoteService;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
@@ -713,8 +712,8 @@
     }
 
     /**
-     * Gets a list of all supported users (i.e., those that pass the {@link #isSupported(UserInfo)}
-     * check).
+     * Gets a list of all supported users (i.e., those that pass the
+     * {@link #isSupportedUser(TargetUser)}check).
      */
     @NonNull
     protected List<UserInfo> getSupportedUsers() {
@@ -723,7 +722,7 @@
         final List<UserInfo> supportedUsers = new ArrayList<>(size);
         for (int i = 0; i < size; i++) {
             final UserInfo userInfo = allUsers[i];
-            if (isSupported(userInfo)) {
+            if (isSupportedUser(new TargetUser(userInfo))) {
                 supportedUsers.add(userInfo);
             }
         }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index c40e8af..9c42152 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -68,8 +68,21 @@
      * @param autofillId {@link AutofillId} of currently focused field.
      * @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request object.
      */
-    public abstract void onCreateInlineSuggestionsRequest(ComponentName componentName,
-            AutofillId autofillId, IInlineSuggestionsRequestCallback cb);
+    public abstract void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
+            ComponentName componentName, AutofillId autofillId,
+            IInlineSuggestionsRequestCallback cb);
+
+    /**
+     * Force switch to the enabled input method by {@code imeId} for current user. If the input
+     * method with {@code imeId} is not enabled or not installed, do nothing.
+     *
+     * @param imeId  The input method ID to be switched to.
+     * @param userId The user ID to be queried.
+     * @return {@code true} if the current input method was successfully switched to the input
+     * method by {@code imeId}; {@code false} the input method with {@code imeId} is not available
+     * to be switched.
+     */
+    public abstract boolean switchToInputMethod(String imeId, @UserIdInt int userId);
 
     /**
      * Fake implementation of {@link InputMethodManagerInternal}.  All the methods do nothing.
@@ -95,8 +108,14 @@
                 }
 
                 @Override
-                public void onCreateInlineSuggestionsRequest(ComponentName componentName,
-                        AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
+                public void onCreateInlineSuggestionsRequest(int userId,
+                        ComponentName componentName, AutofillId autofillId,
+                        IInlineSuggestionsRequestCallback cb) {
+                }
+
+                @Override
+                public boolean switchToInputMethod(String imeId, int userId) {
+                    return false;
                 }
             };
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8b6b614..0bf65bd 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -111,6 +111,7 @@
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 import android.view.autofill.AutofillId;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InlineSuggestionsRequest;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputConnectionInspector;
@@ -142,6 +143,7 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInlineSuggestionsResponseCallback;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethod;
 import com.android.internal.view.IInputMethodClient;
@@ -1790,15 +1792,18 @@
     }
 
     @GuardedBy("mMethodMap")
-    private void onCreateInlineSuggestionsRequestLocked(ComponentName componentName,
-            AutofillId autofillId, IInlineSuggestionsRequestCallback callback) {
-
+    private void onCreateInlineSuggestionsRequestLocked(@UserIdInt int userId,
+            ComponentName componentName, AutofillId autofillId,
+            IInlineSuggestionsRequestCallback callback) {
         final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
         try {
-            if (imi != null && imi.isInlineSuggestionsEnabled() && mCurMethod != null) {
+            if (userId == mSettings.getCurrentUserId() && imi != null
+                    && imi.isInlineSuggestionsEnabled() && mCurMethod != null) {
                 executeOrSendMessage(mCurMethod,
                         mCaller.obtainMessageOOOO(MSG_INLINE_SUGGESTIONS_REQUEST, mCurMethod,
-                                componentName, autofillId, callback));
+                                componentName, autofillId,
+                                new InlineSuggestionsRequestCallbackDecorator(callback,
+                                        imi.getPackageName())));
             } else {
                 callback.onInlineSuggestionsUnsupported();
             }
@@ -1808,6 +1813,42 @@
     }
 
     /**
+     * The decorator which validates the host package name in the
+     * {@link InlineSuggestionsRequest} argument to make sure it matches the IME package name.
+     */
+    private static final class InlineSuggestionsRequestCallbackDecorator
+            extends IInlineSuggestionsRequestCallback.Stub {
+        @NonNull
+        private final IInlineSuggestionsRequestCallback mCallback;
+        @NonNull
+        private final String mImePackageName;
+
+        InlineSuggestionsRequestCallbackDecorator(
+                @NonNull IInlineSuggestionsRequestCallback callback,
+                @NonNull String imePackageName) {
+            mCallback = callback;
+            mImePackageName = imePackageName;
+        }
+
+        @Override
+        public void onInlineSuggestionsUnsupported() throws RemoteException {
+            mCallback.onInlineSuggestionsUnsupported();
+        }
+
+        @Override
+        public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
+                IInlineSuggestionsResponseCallback callback) throws RemoteException {
+            if (!mImePackageName.equals(request.getHostPackageName())) {
+                throw new SecurityException(
+                        "Host package name in the provide request=[" + request.getHostPackageName()
+                                + "] doesn't match the IME package name=[" + mImePackageName
+                                + "].");
+            }
+            mCallback.onInlineSuggestionsRequest(request, callback);
+        }
+    }
+
+    /**
      * @param imiId if null, returns enabled subtypes for the current imi
      * @return enabled subtypes of the specified imi
      */
@@ -4471,10 +4512,43 @@
         }
     }
 
-    private void onCreateInlineSuggestionsRequest(ComponentName componentName,
-            AutofillId autofillId, IInlineSuggestionsRequestCallback callback) {
+    private void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
+            ComponentName componentName, AutofillId autofillId,
+            IInlineSuggestionsRequestCallback callback) {
         synchronized (mMethodMap) {
-            onCreateInlineSuggestionsRequestLocked(componentName, autofillId, callback);
+            onCreateInlineSuggestionsRequestLocked(userId, componentName, autofillId, callback);
+        }
+    }
+
+    private boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
+        synchronized (mMethodMap) {
+            if (userId == mSettings.getCurrentUserId()) {
+                if (!mMethodMap.containsKey(imeId)
+                        || !mSettings.getEnabledInputMethodListLocked()
+                                .contains(mMethodMap.get(imeId))) {
+                    return false; // IME is not is found or not enabled.
+                }
+                setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
+                return true;
+            }
+            final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+            final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+            final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+                    new ArrayMap<>();
+            AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+            queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
+                    methodMap, methodList);
+            final InputMethodSettings settings = new InputMethodSettings(
+                    mContext.getResources(), mContext.getContentResolver(), methodMap,
+                    userId, false);
+            if (!methodMap.containsKey(imeId)
+                    || !settings.getEnabledInputMethodListLocked()
+                            .contains(methodMap.get(imeId))) {
+                return false; // IME is not is found or not enabled.
+            }
+            settings.putSelectedInputMethod(imeId);
+            settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+            return true;
         }
     }
 
@@ -4510,9 +4584,14 @@
         }
 
         @Override
-        public void onCreateInlineSuggestionsRequest(ComponentName componentName,
+        public void onCreateInlineSuggestionsRequest(int userId, ComponentName componentName,
                 AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
-            mService.onCreateInlineSuggestionsRequest(componentName, autofillId, cb);
+            mService.onCreateInlineSuggestionsRequest(userId, componentName, autofillId, cb);
+        }
+
+        @Override
+        public boolean switchToInputMethod(String imeId, int userId) {
+            return mService.switchToInputMethod(imeId, userId);
         }
     }
 
@@ -5065,31 +5144,7 @@
                 if (!userHasDebugPriv(userId, shellCommand)) {
                     continue;
                 }
-                boolean failedToSelectUnknownIme = false;
-                if (userId == mSettings.getCurrentUserId()) {
-                    if (mMethodMap.containsKey(imeId)) {
-                        setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
-                    } else {
-                        failedToSelectUnknownIme = true;
-                    }
-                } else {
-                    final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
-                    final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
-                    final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
-                            new ArrayMap<>();
-                    AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
-                    queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
-                            methodMap, methodList);
-                    final InputMethodSettings settings = new InputMethodSettings(
-                            mContext.getResources(), mContext.getContentResolver(), methodMap,
-                            userId, false);
-                    if (methodMap.containsKey(imeId)) {
-                        settings.putSelectedInputMethod(imeId);
-                        settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
-                    } else {
-                        failedToSelectUnknownIme = true;
-                    }
-                }
+                boolean failedToSelectUnknownIme = !switchToInputMethod(imeId, userId);
                 if (failedToSelectUnknownIme) {
                     error.print("Unknown input method ");
                     error.print(imeId);
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 1f9379c..f09795f 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -191,8 +191,9 @@
                         }
 
                         @Override
-                        public void onCreateInlineSuggestionsRequest(ComponentName componentName,
-                                AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
+                        public void onCreateInlineSuggestionsRequest(int userId,
+                                ComponentName componentName, AutofillId autofillId,
+                                IInlineSuggestionsRequestCallback cb) {
                             try {
                                 //TODO(b/137800469): support multi client IMEs.
                                 cb.onInlineSuggestionsUnsupported();
@@ -200,6 +201,12 @@
                                 Slog.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e);
                             }
                         }
+
+                        @Override
+                        public boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
+                            reportNotSupported();
+                            return false;
+                        }
                     });
         }
 
diff --git a/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java b/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java
index e555e3e..4bf8fe8 100644
--- a/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java
+++ b/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java
@@ -27,30 +27,37 @@
  */
 public class BitTrackedInputStream extends BitInputStream {
 
-    private static int sReadBitsCount;
+    private int mReadBitsCount;
 
     /** Constructor with byte array. */
     public BitTrackedInputStream(byte[] inputStream) {
         super(inputStream);
-        sReadBitsCount = 0;
+        mReadBitsCount = 0;
     }
 
     /** Constructor with input stream. */
     public BitTrackedInputStream(InputStream inputStream) {
         super(inputStream);
-        sReadBitsCount = 0;
+        mReadBitsCount = 0;
     }
 
     /** Obtains an integer value of the next {@code numOfBits}. */
     @Override
     public int getNext(int numOfBits) throws IOException {
-        sReadBitsCount += numOfBits;
+        mReadBitsCount += numOfBits;
         return super.getNext(numOfBits);
     }
 
     /** Returns the current cursor position showing the number of bits that are read. */
     public int getReadBitsCount() {
-        return sReadBitsCount;
+        return mReadBitsCount;
+    }
+
+    /**
+     * Returns true if we can read more rules by checking whether the end index is not reached yet.
+     */
+    public boolean canReadMoreRules(int endIndexBytes) {
+        return mReadBitsCount < endIndexBytes * 8;
     }
 
     /**
@@ -59,11 +66,11 @@
      * Note that the integer parameter specifies the location in bytes -- not bits.
      */
     public void setCursorToByteLocation(int byteLocation) throws IOException {
-        int bitCountToRead = byteLocation * 8 - sReadBitsCount;
+        int bitCountToRead = byteLocation * 8 - mReadBitsCount;
         if (bitCountToRead < 0) {
             throw new IllegalStateException("The byte position is already read.");
         }
         super.getNext(bitCountToRead);
-        sReadBitsCount = byteLocation * 8;
+        mReadBitsCount = byteLocation * 8;
     }
 }
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
index e744326..2f28563 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -105,8 +105,7 @@
             bitTrackedInputStream.setCursorToByteLocation(range.getStartIndex());
 
             // Read the rules until we reach the end index.
-            while (bitTrackedInputStream.hasNext()
-                    && bitTrackedInputStream.getReadBitsCount() < range.getEndIndex()) {
+            while (bitTrackedInputStream.canReadMoreRules(range.getEndIndex())) {
                 if (bitTrackedInputStream.getNext(SIGNAL_BIT) == 1) {
                     parsedRules.add(parseRule(bitTrackedInputStream));
                 }
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
index 8c8450e..453fa5d 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
@@ -23,28 +23,28 @@
  * RuleIndexingController}.
  */
 public class RuleIndexRange {
-    private static int sStartIndex;
-    private static int sEndIndex;
+    private int mStartIndex;
+    private int mEndIndex;
 
     /** Constructor with start and end indexes. */
     public RuleIndexRange(int startIndex, int endIndex) {
-        this.sStartIndex = startIndex;
-        this.sEndIndex = endIndex;
+        this.mStartIndex = startIndex;
+        this.mEndIndex = endIndex;
     }
 
     /** Returns the startIndex. */
     public int getStartIndex() {
-        return sStartIndex;
+        return mStartIndex;
     }
 
     /** Returns the end index. */
     public int getEndIndex() {
-        return sEndIndex;
+        return mEndIndex;
     }
 
     @Override
     public boolean equals(@Nullable Object object) {
-        return sStartIndex == ((RuleIndexRange) object).getStartIndex()
-                && sEndIndex == ((RuleIndexRange) object).getEndIndex();
+        return mStartIndex == ((RuleIndexRange) object).getStartIndex()
+                && mEndIndex == ((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 c971322..03392ab 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
@@ -56,7 +56,7 @@
      * read and evaluated.
      */
     public List<RuleIndexRange> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
-        ArrayList<RuleIndexRange> indexRanges = new ArrayList();
+        List<RuleIndexRange> indexRanges = new ArrayList<>();
 
         // Add the range for package name indexes rules.
         indexRanges.add(
@@ -102,7 +102,7 @@
                         .collect(Collectors.toCollection(TreeSet::new));
 
         String minIndex = keyTreeSet.floor(searchedKey);
-        String maxIndex = keyTreeSet.ceiling(searchedKey);
+        String maxIndex = keyTreeSet.higher(searchedKey);
 
         return new RuleIndexRange(
                 indexMap.get(minIndex == null ? START_INDEXING_KEY : minIndex),
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
index f964d4c..8f53be7 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -48,10 +48,11 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.TreeMap;
+import java.util.stream.Collectors;
 
 /** A helper class to serialize rules from the {@link Rule} model to Binary representation. */
 public class RuleBinarySerializer implements RuleSerializer {
@@ -79,31 +80,37 @@
             throws RuleSerializeException {
         try {
             // Determine the indexing groups and the order of the rules within each indexed group.
-            Map<Integer, TreeMap<String, List<Rule>>> indexedRules =
+            Map<Integer, Map<String, List<Rule>>> indexedRules =
                     RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules);
 
+            // Serialize the rules.
             ByteTrackedOutputStream ruleFileByteTrackedOutputStream =
                     new ByteTrackedOutputStream(rulesFileOutputStream);
-
             serializeRuleFileMetadata(formatVersion, ruleFileByteTrackedOutputStream);
-
-            Map<String, Integer> packageNameIndexes =
-                    serializeRuleList(indexedRules.get(PACKAGE_NAME_INDEXED),
+            LinkedHashMap<String, Integer> packageNameIndexes =
+                    serializeRuleList(
+                            indexedRules.get(PACKAGE_NAME_INDEXED),
                             ruleFileByteTrackedOutputStream);
-            indexingFileOutputStream.write(
-                    serializeIndexes(packageNameIndexes, /* isIndexed= */true));
-
-            Map<String, Integer> appCertificateIndexes =
-                    serializeRuleList(indexedRules.get(APP_CERTIFICATE_INDEXED),
+            LinkedHashMap<String, Integer> appCertificateIndexes =
+                    serializeRuleList(
+                            indexedRules.get(APP_CERTIFICATE_INDEXED),
                             ruleFileByteTrackedOutputStream);
-            indexingFileOutputStream.write(
-                    serializeIndexes(appCertificateIndexes, /* isIndexed= */true));
-
-            Map<String, Integer> unindexedRulesIndexes =
-                    serializeRuleList(indexedRules.get(NOT_INDEXED),
+            LinkedHashMap<String, Integer> unindexedRulesIndexes =
+                    serializeRuleList(
+                            indexedRules.get(NOT_INDEXED),
                             ruleFileByteTrackedOutputStream);
-            indexingFileOutputStream.write(
-                    serializeIndexes(unindexedRulesIndexes, /* isIndexed= */false));
+
+            // Serialize their indexes.
+            BitOutputStream indexingBitOutputStream = new BitOutputStream();
+            serializeIndexGroup(packageNameIndexes, indexingBitOutputStream, /* isIndexed= */true);
+            serializeIndexGroup(appCertificateIndexes, indexingBitOutputStream, /* isIndexed= */
+                    true);
+            serializeIndexGroup(unindexedRulesIndexes, indexingBitOutputStream, /* isIndexed= */
+                    false);
+            // TODO(b/147609625): This dummy bit is set for fixing the padding issue. Remove when
+            // the issue is fixed and correct the tests that does this padding too.
+            indexingBitOutputStream.setNext();
+            indexingFileOutputStream.write(indexingBitOutputStream.toByteArray());
         } catch (Exception e) {
             throw new RuleSerializeException(e.getMessage(), e);
         }
@@ -119,24 +126,25 @@
         outputStream.write(bitOutputStream.toByteArray());
     }
 
-    private Map<String, Integer> serializeRuleList(TreeMap<String, List<Rule>> rulesMap,
-            ByteTrackedOutputStream outputStream)
+    private LinkedHashMap<String, Integer> serializeRuleList(
+            Map<String, List<Rule>> rulesMap, ByteTrackedOutputStream outputStream)
             throws IOException {
         Preconditions.checkArgument(rulesMap != null,
                 "serializeRuleList should never be called with null rule list.");
 
         BitOutputStream bitOutputStream = new BitOutputStream();
-        Map<String, Integer> indexMapping = new TreeMap();
-        int indexTracker = 0;
-
+        LinkedHashMap<String, Integer> indexMapping = new LinkedHashMap();
         indexMapping.put(START_INDEXING_KEY, outputStream.getWrittenBytesCount());
-        for (Map.Entry<String, List<Rule>> entry : rulesMap.entrySet()) {
+
+        List<String> sortedKeys = rulesMap.keySet().stream().sorted().collect(Collectors.toList());
+        int indexTracker = 0;
+        for (String key : sortedKeys) {
             if (indexTracker >= INDEXING_BLOCK_SIZE) {
-                indexMapping.put(entry.getKey(), outputStream.getWrittenBytesCount());
+                indexMapping.put(key, outputStream.getWrittenBytesCount());
                 indexTracker = 0;
             }
 
-            for (Rule rule : entry.getValue()) {
+            for (Rule rule : rulesMap.get(key)) {
                 bitOutputStream.clear();
                 serializeRule(rule, bitOutputStream);
                 outputStream.write(bitOutputStream.toByteArray());
@@ -220,12 +228,14 @@
         }
     }
 
-    private byte[] serializeIndexes(Map<String, Integer> indexes, boolean isIndexed) {
-        BitOutputStream bitOutputStream = new BitOutputStream();
+    private void serializeIndexGroup(
+            LinkedHashMap<String, Integer> indexes,
+            BitOutputStream bitOutputStream,
+            boolean isIndexed) {
 
         // Output the starting location of this indexing group.
-        serializeStringValue(START_INDEXING_KEY, /* isHashedValue= */false,
-                bitOutputStream);
+        serializeStringValue(
+                START_INDEXING_KEY, /* isHashedValue= */false, bitOutputStream);
         serializeIntValue(indexes.get(START_INDEXING_KEY), bitOutputStream);
 
         // If the group is indexed, output the locations of the indexes.
@@ -243,8 +253,6 @@
         // Output the end location of this indexing group.
         serializeStringValue(END_INDEXING_KEY, /*isHashedValue= */ false, bitOutputStream);
         serializeIntValue(indexes.get(END_INDEXING_KEY), bitOutputStream);
-
-        return bitOutputStream.toByteArray();
     }
 
     private void serializeStringValue(
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java
index dd871e2..2cbd4ede 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java
@@ -28,6 +28,8 @@
     static final int PACKAGE_NAME_INDEXED = 1;
     static final int APP_CERTIFICATE_INDEXED = 2;
 
+    static final String DEFAULT_RULE_KEY = "N/A";
+
     /** Represents which indexed file the rule should be located. */
     @IntDef(
             value = {
@@ -45,7 +47,7 @@
     /** Constructor without a ruleKey for {@code NOT_INDEXED}. */
     RuleIndexingDetails(@IndexType int indexType) {
         this.mIndexType = indexType;
-        this.mRuleKey = null;
+        this.mRuleKey = DEFAULT_RULE_KEY;
     }
 
     /** Constructor with a ruleKey for indexed rules. */
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
index cbc365e..7d9a901 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
@@ -30,30 +30,27 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.TreeMap;
 
 /** A helper class for identifying the indexing type and key of a given rule. */
 class RuleIndexingDetailsIdentifier {
 
-    private static final String DEFAULT_RULE_KEY = "N/A";
-
     /**
      * Splits a given rule list into three indexing categories. Each rule category is returned as a
      * TreeMap that is sorted by their indexing keys -- where keys correspond to package name for
      * PACKAGE_NAME_INDEXED rules, app certificate for APP_CERTIFICATE_INDEXED rules and N/A for
      * NOT_INDEXED rules.
      */
-    public static Map<Integer, TreeMap<String, List<Rule>>> splitRulesIntoIndexBuckets(
+    public static Map<Integer, Map<String, List<Rule>>> splitRulesIntoIndexBuckets(
             List<Rule> rules) {
         if (rules == null) {
             throw new IllegalArgumentException(
                     "Index buckets cannot be created for null rule list.");
         }
 
-        Map<Integer, TreeMap<String, List<Rule>>> typeOrganizedRuleMap = new HashMap();
-        typeOrganizedRuleMap.put(NOT_INDEXED, new TreeMap());
-        typeOrganizedRuleMap.put(PACKAGE_NAME_INDEXED, new TreeMap());
-        typeOrganizedRuleMap.put(APP_CERTIFICATE_INDEXED, new TreeMap());
+        Map<Integer, Map<String, List<Rule>>> typeOrganizedRuleMap = new HashMap();
+        typeOrganizedRuleMap.put(NOT_INDEXED, new HashMap());
+        typeOrganizedRuleMap.put(PACKAGE_NAME_INDEXED, new HashMap<>());
+        typeOrganizedRuleMap.put(APP_CERTIFICATE_INDEXED, new HashMap<>());
 
         // Split the rules into the appropriate indexed pattern. The Tree Maps help us to keep the
         // entries sorted by their index key.
@@ -66,21 +63,14 @@
                         String.format("Malformed rule identified. [%s]", rule.toString()));
             }
 
-            String ruleKey =
-                    indexingDetails.getIndexType() != NOT_INDEXED
-                            ? indexingDetails.getRuleKey()
-                            : DEFAULT_RULE_KEY;
+            int ruleIndexType = indexingDetails.getIndexType();
+            String ruleKey = indexingDetails.getRuleKey();
 
-            if (!typeOrganizedRuleMap.get(indexingDetails.getIndexType()).containsKey(ruleKey)) {
-                typeOrganizedRuleMap
-                        .get(indexingDetails.getIndexType())
-                        .put(ruleKey, new ArrayList());
+            if (!typeOrganizedRuleMap.get(ruleIndexType).containsKey(ruleKey)) {
+                typeOrganizedRuleMap.get(ruleIndexType).put(ruleKey, new ArrayList());
             }
 
-            typeOrganizedRuleMap
-                    .get(indexingDetails.getIndexType())
-                    .get(ruleKey)
-                    .add(rule);
+            typeOrganizedRuleMap.get(ruleIndexType).get(ruleKey).add(rule);
         }
 
         return typeOrganizedRuleMap;
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
index 4194432..8f164e6 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
@@ -35,7 +35,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.TreeMap;
+import java.util.stream.Collectors;
 
 /** A helper class to serialize rules from the {@link Rule} model to Xml representation. */
 public class RuleXmlSerializer implements RuleSerializer {
@@ -90,7 +90,7 @@
             throws RuleSerializeException {
         try {
             // Determine the indexing groups and the order of the rules within each indexed group.
-            Map<Integer, TreeMap<String, List<Rule>>> indexedRules =
+            Map<Integer, Map<String, List<Rule>>> indexedRules =
                     RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules);
 
             // Write the XML formatted rules in order.
@@ -107,11 +107,12 @@
         }
     }
 
-    private void serializeRuleList(TreeMap<String, List<Rule>> rulesMap,
-            XmlSerializer xmlSerializer)
+    private void serializeRuleList(Map<String, List<Rule>> rulesMap, XmlSerializer xmlSerializer)
             throws IOException {
-        for (Map.Entry<String, List<Rule>> entry : rulesMap.entrySet()) {
-            for (Rule rule : entry.getValue()) {
+        List<String> sortedKeyList =
+                rulesMap.keySet().stream().sorted().collect(Collectors.toList());
+        for (String key : sortedKeyList) {
+            for (Rule rule : rulesMap.get(key)) {
                 serializeRule(rule, xmlSerializer);
             }
         }
diff --git a/services/core/java/com/android/server/location/NtpTimeHelper.java b/services/core/java/com/android/server/location/NtpTimeHelper.java
index 67841ac..d2296ea 100644
--- a/services/core/java/com/android/server/location/NtpTimeHelper.java
+++ b/services/core/java/com/android/server/location/NtpTimeHelper.java
@@ -130,7 +130,8 @@
 
         // force refresh NTP cache when outdated
         boolean refreshSuccess = true;
-        if (mNtpTime.getCacheAge() >= NTP_INTERVAL) {
+        NtpTrustedTime.TimeResult ntpResult = mNtpTime.getCachedTimeResult();
+        if (ntpResult == null || ntpResult.getAgeMillis() >= NTP_INTERVAL) {
             // Blocking network operation.
             refreshSuccess = mNtpTime.forceRefresh();
         }
@@ -140,17 +141,17 @@
 
             // only update when NTP time is fresh
             // If refreshSuccess is false, cacheAge does not drop down.
-            if (mNtpTime.getCacheAge() < NTP_INTERVAL) {
-                long time = mNtpTime.getCachedNtpTime();
-                long timeReference = mNtpTime.getCachedNtpTimeReference();
-                long certainty = mNtpTime.getCacheCertainty();
+            ntpResult = mNtpTime.getCachedTimeResult();
+            if (ntpResult != null && ntpResult.getAgeMillis() < NTP_INTERVAL) {
+                long time = ntpResult.getTimeMillis();
+                long timeReference = ntpResult.getElapsedRealtimeMillis();
+                long certainty = ntpResult.getCertaintyMillis();
 
                 if (DEBUG) {
                     long now = System.currentTimeMillis();
                     Log.d(TAG, "NTP server returned: "
-                            + time + " (" + new Date(time)
-                            + ") reference: " + timeReference
-                            + " certainty: " + certainty
+                            + time + " (" + new Date(time) + ")"
+                            + " ntpResult: " + ntpResult
                             + " system time offset: " + (time - now));
                 }
 
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
new file mode 100644
index 0000000..5191833
--- /dev/null
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.annotation.NonNull;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.MediaRoute2Info;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+class BluetoothRouteProvider {
+    private static final String TAG = "BTRouteProvider";
+    private static BluetoothRouteProvider sInstance;
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    BluetoothA2dp mA2dpProfile;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    BluetoothHearingAid mHearingAidProfile;
+
+    private final Context mContext;
+    private final BluetoothAdapter mBluetoothAdapter;
+    private final BluetoothRoutesUpdatedListener mListener;
+    private final Map<String, BluetoothEventReceiver> mEventReceiverMap = new HashMap<>();
+    private final IntentFilter mIntentFilter = new IntentFilter();
+    private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
+    private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener();
+
+    private BluetoothDevice mActiveDevice = null;
+
+    static synchronized BluetoothRouteProvider getInstance(@NonNull Context context,
+            @NonNull BluetoothRoutesUpdatedListener listener) {
+        Objects.requireNonNull(context);
+        Objects.requireNonNull(listener);
+
+        if (sInstance == null) {
+            BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+            if (btAdapter == null) {
+                return null;
+            }
+            sInstance = new BluetoothRouteProvider(context, btAdapter, listener);
+        }
+        return sInstance;
+    }
+
+    private BluetoothRouteProvider(Context context, BluetoothAdapter btAdapter,
+            BluetoothRoutesUpdatedListener listener) {
+        mContext = context;
+        mBluetoothAdapter = btAdapter;
+        mListener = listener;
+        buildBluetoothRoutes();
+
+        mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
+        mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
+
+        // Bluetooth on/off broadcasts
+        addEventReceiver(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedReceiver());
+
+        // Pairing broadcasts
+        addEventReceiver(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedReceiver());
+
+        DeviceStateChangedRecevier deviceStateChangedReceiver = new DeviceStateChangedRecevier();
+        addEventReceiver(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED, deviceStateChangedReceiver);
+        addEventReceiver(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, deviceStateChangedReceiver);
+        addEventReceiver(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED,
+                deviceStateChangedReceiver);
+        addEventReceiver(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED,
+                deviceStateChangedReceiver);
+
+        mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, null, null);
+    }
+
+    private void addEventReceiver(String action, BluetoothEventReceiver eventReceiver) {
+        mEventReceiverMap.put(action, eventReceiver);
+        mIntentFilter.addAction(action);
+    }
+
+    private void buildBluetoothRoutes() {
+        mBluetoothRoutes.clear();
+        for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
+            if (device.isConnected()) {
+                BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
+                mBluetoothRoutes.put(device.getAddress(), newBtRoute);
+            }
+        }
+    }
+
+    @NonNull List<MediaRoute2Info> getBluetoothRoutes() {
+        ArrayList<MediaRoute2Info> routes = new ArrayList<>();
+        for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
+            routes.add(btRoute.route);
+        }
+        return routes;
+    }
+
+    private void notifyBluetoothRoutesUpdated() {
+        if (mListener != null) {
+            mListener.onBluetoothRoutesUpdated(getBluetoothRoutes());
+        }
+    }
+
+    private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
+        BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo();
+        newBtRoute.btDevice = device;
+        newBtRoute.route = new MediaRoute2Info.Builder(device.getAddress(), device.getName())
+                .addFeature(SystemMediaRoute2Provider.TYPE_LIVE_AUDIO)
+                .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
+                .setDescription(mContext.getResources().getText(
+                        R.string.bluetooth_a2dp_audio_route_name).toString())
+                .build();
+        newBtRoute.connectedProfiles = new SparseBooleanArray();
+        return newBtRoute;
+    }
+
+    private void setRouteConnectionStateForDevice(BluetoothDevice device,
+            @MediaRoute2Info.ConnectionState int state) {
+        if (device == null) {
+            Log.w(TAG, "setRouteConnectionStateForDevice: device shouldn't be null");
+            return;
+        }
+        BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
+        if (btRoute == null) {
+            Log.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null");
+            return;
+        }
+        if (btRoute.route.getConnectionState() != state) {
+            btRoute.route = new MediaRoute2Info.Builder(btRoute.route)
+                    .setConnectionState(state).build();
+        }
+    }
+
+    interface BluetoothRoutesUpdatedListener {
+        void onBluetoothRoutesUpdated(@NonNull List<MediaRoute2Info> routes);
+    }
+
+    private class BluetoothRouteInfo {
+        public BluetoothDevice btDevice;
+        public MediaRoute2Info route;
+        public SparseBooleanArray connectedProfiles;
+    }
+
+    // These callbacks run on the main thread.
+    private final class BluetoothProfileListener implements BluetoothProfile.ServiceListener {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            switch (profile) {
+                case BluetoothProfile.A2DP:
+                    mA2dpProfile = (BluetoothA2dp) proxy;
+                    break;
+                case BluetoothProfile.HEARING_AID:
+                    mHearingAidProfile = (BluetoothHearingAid) proxy;
+                    break;
+                default:
+                    return;
+            }
+            for (BluetoothDevice device : proxy.getConnectedDevices()) {
+                BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
+                if (btRoute == null) {
+                    btRoute = createBluetoothRoute(device);
+                    mBluetoothRoutes.put(device.getAddress(), btRoute);
+                }
+                btRoute.connectedProfiles.put(profile, true);
+            }
+        }
+
+        public void onServiceDisconnected(int profile) {
+            switch (profile) {
+                case BluetoothProfile.A2DP:
+                    mA2dpProfile = null;
+                    break;
+                case BluetoothProfile.HEARING_AID:
+                    mHearingAidProfile = null;
+                    break;
+                default:
+                    return;
+            }
+        }
+    }
+    private class BluetoothBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+            BluetoothEventReceiver receiver = mEventReceiverMap.get(action);
+            if (receiver != null) {
+                receiver.onReceive(context, intent, device);
+            }
+        }
+    }
+
+    private interface BluetoothEventReceiver {
+        void onReceive(Context context, Intent intent, BluetoothDevice device);
+    }
+
+    private class AdapterStateChangedReceiver implements BluetoothEventReceiver {
+        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+            if (state == BluetoothAdapter.STATE_OFF
+                    || state == BluetoothAdapter.STATE_TURNING_OFF) {
+                mBluetoothRoutes.clear();
+                notifyBluetoothRoutesUpdated();
+            } else if (state == BluetoothAdapter.STATE_ON) {
+                buildBluetoothRoutes();
+                if (!mBluetoothRoutes.isEmpty()) {
+                    notifyBluetoothRoutesUpdated();
+                }
+            }
+        }
+    }
+
+    private class BondStateChangedReceiver implements BluetoothEventReceiver {
+        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+            int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+                    BluetoothDevice.ERROR);
+            BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
+            if (bondState == BluetoothDevice.BOND_BONDED && btRoute == null) {
+                btRoute = createBluetoothRoute(device);
+                if (mA2dpProfile != null && mA2dpProfile.getConnectedDevices().contains(device)) {
+                    btRoute.connectedProfiles.put(BluetoothProfile.A2DP, true);
+                }
+                if (mHearingAidProfile != null
+                        && mHearingAidProfile.getConnectedDevices().contains(device)) {
+                    btRoute.connectedProfiles.put(BluetoothProfile.HEARING_AID, true);
+                }
+                mBluetoothRoutes.put(device.getAddress(), btRoute);
+                notifyBluetoothRoutesUpdated();
+            } else if (bondState == BluetoothDevice.BOND_NONE
+                    && mBluetoothRoutes.remove(device.getAddress()) != null) {
+                notifyBluetoothRoutesUpdated();
+            }
+        }
+    }
+
+    private class DeviceStateChangedRecevier implements BluetoothEventReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+            switch (intent.getAction()) {
+                case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
+                    String prevActiveDeviceAddress =
+                            (mActiveDevice == null) ? null : mActiveDevice.getAddress();
+                    String curActiveDeviceAddress =
+                            (device == null) ? null : device.getAddress();
+                    if (!TextUtils.equals(prevActiveDeviceAddress, curActiveDeviceAddress)) {
+                        if (mActiveDevice != null) {
+                            setRouteConnectionStateForDevice(mActiveDevice,
+                                    MediaRoute2Info.CONNECTION_STATE_DISCONNECTED);
+                        }
+                        if (device != null) {
+                            setRouteConnectionStateForDevice(device,
+                                    MediaRoute2Info.CONNECTION_STATE_CONNECTED);
+                        }
+                        notifyBluetoothRoutesUpdated();
+                        mActiveDevice = device;
+                    }
+                    break;
+                case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
+                    handleConnectionStateChanged(BluetoothProfile.A2DP, intent, device);
+                    break;
+            }
+        }
+
+        private void handleConnectionStateChanged(int profile, Intent intent,
+                BluetoothDevice device) {
+            int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+            BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
+            if (state == BluetoothProfile.STATE_CONNECTED) {
+                if (btRoute == null) {
+                    btRoute = createBluetoothRoute(device);
+                    mBluetoothRoutes.put(device.getAddress(), btRoute);
+                    btRoute.connectedProfiles.put(profile, true);
+                    notifyBluetoothRoutesUpdated();
+                } else {
+                    btRoute.connectedProfiles.put(profile, true);
+                }
+            } else if (state == BluetoothProfile.STATE_DISCONNECTING
+                    || state == BluetoothProfile.STATE_DISCONNECTED) {
+                btRoute.connectedProfiles.delete(profile);
+                if (btRoute.connectedProfiles.size() == 0) {
+                    mBluetoothRoutes.remove(device.getAddress());
+                    if (mActiveDevice != null
+                            && TextUtils.equals(mActiveDevice.getAddress(), device.getAddress())) {
+                        mActiveDevice = null;
+                    }
+                    notifyBluetoothRoutesUpdated();
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 9c9a412..0d315cd 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -21,7 +21,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.media.MediaRoute2ProviderInfo;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -34,7 +34,7 @@
 
     Callback mCallback;
     private volatile MediaRoute2ProviderInfo mProviderInfo;
-    private volatile List<RouteSessionInfo> mSessionInfos = Collections.emptyList();
+    private volatile List<RoutingSessionInfo> mSessionInfos = Collections.emptyList();
 
     MediaRoute2Provider(@NonNull ComponentName componentName) {
         mComponentName = Objects.requireNonNull(componentName, "Component name must not be null.");
@@ -68,12 +68,12 @@
     }
 
     @NonNull
-    public List<RouteSessionInfo> getSessionInfos() {
+    public List<RoutingSessionInfo> getSessionInfos() {
         return mSessionInfos;
     }
 
-    void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo,
-            List<RouteSessionInfo> sessionInfos) {
+    void setProviderState(MediaRoute2ProviderInfo providerInfo,
+            List<RoutingSessionInfo> sessionInfos) {
         if (providerInfo == null) {
             mProviderInfo = null;
         } else {
@@ -81,20 +81,28 @@
                     .setUniqueId(mUniqueId)
                     .build();
         }
-        List<RouteSessionInfo> sessionInfoWithProviderId = new ArrayList<RouteSessionInfo>();
-        for (RouteSessionInfo sessionInfo : sessionInfos) {
+        List<RoutingSessionInfo> sessionInfoWithProviderId = new ArrayList<RoutingSessionInfo>();
+        for (RoutingSessionInfo sessionInfo : sessionInfos) {
             sessionInfoWithProviderId.add(
-                    new RouteSessionInfo.Builder(sessionInfo)
+                    new RoutingSessionInfo.Builder(sessionInfo)
                             .setProviderId(mUniqueId)
                             .build());
         }
         mSessionInfos = sessionInfoWithProviderId;
+    }
 
+    void notifyProviderState() {
         if (mCallback != null) {
             mCallback.onProviderStateChanged(this);
         }
     }
 
+    void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo,
+            List<RoutingSessionInfo> sessionInfos) {
+        setProviderState(providerInfo, sessionInfos);
+        notifyProviderState();
+    }
+
     public boolean hasComponentName(String packageName, String className) {
         return mComponentName.getPackageName().equals(packageName)
                 && mComponentName.getClassName().equals(className);
@@ -103,12 +111,12 @@
     public interface Callback {
         void onProviderStateChanged(@Nullable MediaRoute2Provider provider);
         void onSessionCreated(@NonNull MediaRoute2Provider provider,
-                @Nullable RouteSessionInfo sessionInfo, long requestId);
+                @Nullable RoutingSessionInfo sessionInfo, long requestId);
         // TODO: Remove this when MediaRouter2ServiceImpl notifies clients of session changes.
         void onSessionInfoChanged(@NonNull MediaRoute2Provider provider,
-                @NonNull RouteSessionInfo sessionInfo);
+                @NonNull RoutingSessionInfo sessionInfo);
         // TODO: Call this when service actually notifies of session release.
         void onSessionReleased(@NonNull MediaRoute2Provider provider,
-                @NonNull RouteSessionInfo sessionInfo);
+                @NonNull RoutingSessionInfo sessionInfo);
     }
 }
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index 6359835..c0ad46f 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -26,7 +26,7 @@
 import android.media.IMediaRoute2ProviderClient;
 import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRoute2ProviderService;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
@@ -270,7 +270,7 @@
     }
 
     private void onProviderStateUpdated(Connection connection,
-            MediaRoute2ProviderInfo providerInfo, List<RouteSessionInfo> sessionInfos) {
+            MediaRoute2ProviderInfo providerInfo, List<RoutingSessionInfo> sessionInfos) {
         if (mActiveConnection != connection) {
             return;
         }
@@ -280,20 +280,20 @@
         setAndNotifyProviderState(providerInfo, sessionInfos);
     }
 
-    private void onSessionCreated(Connection connection, @Nullable RouteSessionInfo sessionInfo,
+    private void onSessionCreated(Connection connection, @Nullable RoutingSessionInfo sessionInfo,
             long requestId) {
         if (mActiveConnection != connection) {
             return;
         }
         if (sessionInfo != null) {
-            sessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+            sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
                     .setProviderId(getUniqueId())
                     .build();
         }
         mCallback.onSessionCreated(this, sessionInfo, requestId);
     }
 
-    private void onSessionInfoChanged(Connection connection, RouteSessionInfo sessionInfo) {
+    private void onSessionInfoChanged(Connection connection, RoutingSessionInfo sessionInfo) {
         if (mActiveConnection != connection) {
             return;
         }
@@ -303,7 +303,7 @@
             return;
         }
 
-        sessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+        sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
                 .setProviderId(getUniqueId())
                 .build();
 
@@ -422,17 +422,17 @@
         }
 
         void postProviderStateUpdated(MediaRoute2ProviderInfo providerInfo,
-                List<RouteSessionInfo> sessionInfos) {
+                List<RoutingSessionInfo> sessionInfos) {
             mHandler.post(() -> onProviderStateUpdated(Connection.this,
                     providerInfo, sessionInfos));
         }
 
-        void postSessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) {
+        void postSessionCreated(@Nullable RoutingSessionInfo sessionInfo, long requestId) {
             mHandler.post(() -> onSessionCreated(Connection.this, sessionInfo,
                     requestId));
         }
 
-        void postSessionInfoChanged(RouteSessionInfo sessionInfo) {
+        void postSessionInfoChanged(RoutingSessionInfo sessionInfo) {
             mHandler.post(() -> onSessionInfoChanged(Connection.this, sessionInfo));
         }
     }
@@ -450,7 +450,7 @@
 
         @Override
         public void updateState(MediaRoute2ProviderInfo providerInfo,
-                List<RouteSessionInfo> sessionInfos) {
+                List<RoutingSessionInfo> sessionInfos) {
             Connection connection = mConnectionRef.get();
             if (connection != null) {
                 connection.postProviderStateUpdated(providerInfo, sessionInfos);
@@ -458,7 +458,7 @@
         }
 
         @Override
-        public void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) {
+        public void notifySessionCreated(@Nullable RoutingSessionInfo sessionInfo, long requestId) {
             Connection connection = mConnectionRef.get();
             if (connection != null) {
                 connection.postSessionCreated(sessionInfo, requestId);
@@ -466,7 +466,7 @@
         }
 
         @Override
-        public void notifySessionInfoChanged(RouteSessionInfo sessionInfo) {
+        public void notifySessionInfoChanged(RoutingSessionInfo sessionInfo) {
             Connection connection = mConnectionRef.get();
             if (connection != null) {
                 connection.postSessionInfoChanged(sessionInfo);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 487ab52..c48c90d 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -31,8 +31,8 @@
 import android.media.IMediaRouter2Manager;
 import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
-import android.media.RouteDiscoveryRequest;
-import android.media.RouteSessionInfo;
+import android.media.RouteDiscoveryPreference;
+import android.media.RoutingSessionInfo;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -178,18 +178,18 @@
     }
 
     public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
-            String routeType, int requestId) {
+            String routeFeature, int requestId) {
         Objects.requireNonNull(client, "client must not be null");
         Objects.requireNonNull(route, "route must not be null");
-        if (TextUtils.isEmpty(routeType)) {
-            throw new IllegalArgumentException("routeType must not be empty");
+        if (TextUtils.isEmpty(routeFeature)) {
+            throw new IllegalArgumentException("routeFeature must not be empty");
         }
 
         final long token = Binder.clearCallingIdentity();
 
         try {
             synchronized (mLock) {
-                requestCreateSessionLocked(client, route, routeType, requestId);
+                requestCreateSessionLocked(client, route, routeFeature, requestId);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -284,15 +284,15 @@
     }
 
     public void setDiscoveryRequest2(@NonNull IMediaRouter2Client client,
-            @NonNull RouteDiscoveryRequest request) {
+            @NonNull RouteDiscoveryPreference preference) {
         Objects.requireNonNull(client, "client must not be null");
-        Objects.requireNonNull(request, "request must not be null");
+        Objects.requireNonNull(preference, "preference must not be null");
 
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
                 Client2Record clientRecord = mAllClientRecords.get(client.asBinder());
-                setDiscoveryRequestLocked(clientRecord, request);
+                setDiscoveryRequestLocked(clientRecord, preference);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -370,7 +370,7 @@
     }
 
     @NonNull
-    public List<RouteSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
+    public List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
@@ -450,7 +450,7 @@
     }
 
     private void requestCreateSessionLocked(@NonNull IMediaRouter2Client client,
-            @NonNull MediaRoute2Info route, @NonNull String routeType, long requestId) {
+            @NonNull MediaRoute2Info route, @NonNull String routeFeature, long requestId) {
         final IBinder binder = client.asBinder();
         final Client2Record clientRecord = mAllClientRecords.get(binder);
 
@@ -463,7 +463,7 @@
             clientRecord.mUserRecord.mHandler.sendMessage(
                     obtainMessage(UserHandler::requestCreateSessionOnHandler,
                             clientRecord.mUserRecord.mHandler,
-                            clientRecord, route, routeType, requestId));
+                            clientRecord, route, routeFeature, requestId));
         }
     }
 
@@ -519,13 +519,13 @@
     }
 
     private void setDiscoveryRequestLocked(Client2Record clientRecord,
-            RouteDiscoveryRequest discoveryRequest) {
+            RouteDiscoveryPreference discoveryRequest) {
         if (clientRecord != null) {
-            if (clientRecord.mDiscoveryRequest.equals(discoveryRequest)) {
+            if (clientRecord.mDiscoveryPreference.equals(discoveryRequest)) {
                 return;
             }
 
-            clientRecord.mDiscoveryRequest = discoveryRequest;
+            clientRecord.mDiscoveryPreference = discoveryRequest;
             clientRecord.mUserRecord.mHandler.sendMessage(
                     obtainMessage(UserHandler::updateClientUsage,
                             clientRecord.mUserRecord.mHandler, clientRecord));
@@ -622,9 +622,9 @@
             }
             long uniqueRequestId = toUniqueRequestId(managerRecord.mClientId, requestId);
             if (clientRecord != null && managerRecord.mTrusted) {
-                //TODO: select route type properly
+                //TODO: select route feature properly
                 requestCreateSessionLocked(clientRecord.mClient, route,
-                        route.getRouteTypes().get(0), uniqueRequestId);
+                        route.getFeatures().get(0), uniqueRequestId);
             }
         }
     }
@@ -653,7 +653,7 @@
         }
     }
 
-    private List<RouteSessionInfo> getActiveSessionsLocked(IMediaRouter2Manager manager) {
+    private List<RoutingSessionInfo> getActiveSessionsLocked(IMediaRouter2Manager manager) {
         final IBinder binder = manager.asBinder();
         ManagerRecord managerRecord = mAllManagerRecords.get(binder);
 
@@ -661,7 +661,7 @@
             return Collections.emptyList();
         }
 
-        List<RouteSessionInfo> sessionInfos = new ArrayList<>();
+        List<RoutingSessionInfo> sessionInfos = new ArrayList<>();
         for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mMediaProviders) {
             sessionInfos.addAll(provider.getSessionInfos());
         }
@@ -742,7 +742,7 @@
         public final boolean mTrusted;
         public final int mClientId;
 
-        public RouteDiscoveryRequest mDiscoveryRequest;
+        public RouteDiscoveryPreference mDiscoveryPreference;
         public boolean mIsManagerSelecting;
         public MediaRoute2Info mSelectingRoute;
         public MediaRoute2Info mSelectedRoute;
@@ -752,7 +752,7 @@
             mUserRecord = userRecord;
             mPackageName = packageName;
             mSelectRouteSequenceNumbers = new ArrayList<>();
-            mDiscoveryRequest = RouteDiscoveryRequest.EMPTY;
+            mDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
             mClient = client;
             mUid = uid;
             mPid = pid;
@@ -876,20 +876,21 @@
 
         @Override
         public void onSessionCreated(@NonNull MediaRoute2Provider provider,
-                @Nullable RouteSessionInfo sessionInfo, long requestId) {
+                @Nullable RoutingSessionInfo sessionInfo, long requestId) {
             sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionCreatedOnHandler,
                     this, provider, sessionInfo, requestId));
         }
 
         @Override
         public void onSessionInfoChanged(@NonNull MediaRoute2Provider provider,
-                @NonNull RouteSessionInfo sessionInfo) {
+                @NonNull RoutingSessionInfo sessionInfo) {
             sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionInfoChangedOnHandler,
                     this, provider, sessionInfo));
         }
 
         @Override
-        public void onSessionReleased(MediaRoute2Provider provider, RouteSessionInfo sessionInfo) {
+        public void onSessionReleased(MediaRoute2Provider provider,
+                RoutingSessionInfo sessionInfo) {
             sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionReleasedOnHandler,
                     this, provider, sessionInfo));
         }
@@ -932,7 +933,7 @@
                         Slog.w(TAG, "Ignoring invalid route : " + route);
                         continue;
                     }
-                    MediaRoute2Info prevRoute = prevInfo.getRoute(route.getId());
+                    MediaRoute2Info prevRoute = prevInfo.getRoute(route.getOriginalId());
 
                     if (prevRoute != null) {
                         if (!Objects.equals(prevRoute, route)) {
@@ -978,7 +979,7 @@
         }
 
         private void requestCreateSessionOnHandler(Client2Record clientRecord,
-                MediaRoute2Info route, String routeType, long requestId) {
+                MediaRoute2Info route, String routeFeature, long requestId) {
 
             final MediaRoute2Provider provider = findProvider(route.getProviderId());
             if (provider == null) {
@@ -988,20 +989,20 @@
                 return;
             }
 
-            if (!route.getRouteTypes().contains(routeType)) {
+            if (!route.getFeatures().contains(routeFeature)) {
                 Slog.w(TAG, "Ignoring session creation request since the given route=" + route
-                        + " doesn't support the given type=" + routeType);
+                        + " doesn't support the given feature=" + routeFeature);
                 notifySessionCreationFailed(clientRecord, toClientRequestId(requestId));
                 return;
             }
 
             // TODO: Apply timeout for each request (How many seconds should we wait?)
             SessionCreationRequest request = new SessionCreationRequest(
-                    clientRecord, route, routeType, requestId);
+                    clientRecord, route, routeFeature, requestId);
             mSessionCreationRequests.add(request);
 
             provider.requestCreateSession(clientRecord.mPackageName, route.getOriginalId(),
-                    routeType, requestId);
+                    routeFeature, requestId);
         }
 
         private void selectRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1131,7 +1132,7 @@
         }
 
         private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider,
-                @Nullable RouteSessionInfo sessionInfo, long requestId) {
+                @Nullable RoutingSessionInfo sessionInfo, long requestId) {
             SessionCreationRequest matchingRequest = null;
 
             for (SessionCreationRequest request : mSessionCreationRequests) {
@@ -1159,16 +1160,16 @@
             }
 
             String originalRouteId = matchingRequest.mRoute.getId();
-            String originalRouteType = matchingRequest.mRouteType;
+            String originalRouteFeature = matchingRequest.mRouteFeature;
             Client2Record client2Record = matchingRequest.mClientRecord;
 
             if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)
-                    || !TextUtils.equals(originalRouteType,
-                        sessionInfo.getRouteType())) {
+                    || !TextUtils.equals(originalRouteFeature,
+                        sessionInfo.getRouteFeature())) {
                 Slog.w(TAG, "Created session doesn't match the original request."
                         + " originalRouteId=" + originalRouteId
-                        + ", originalRouteType=" + originalRouteType + ", requestId=" + requestId
-                        + ", sessionInfo=" + sessionInfo);
+                        + ", originalRouteFeature=" + originalRouteFeature
+                        + ", requestId=" + requestId + ", sessionInfo=" + sessionInfo);
                 notifySessionCreationFailed(matchingRequest.mClientRecord,
                         toClientRequestId(requestId));
                 return;
@@ -1182,7 +1183,7 @@
         }
 
         private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
-                @NonNull RouteSessionInfo sessionInfo) {
+                @NonNull RoutingSessionInfo sessionInfo) {
 
             Client2Record client2Record = mSessionToClientMap.get(
                     sessionInfo.getId());
@@ -1196,7 +1197,7 @@
         }
 
         private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider,
-                @NonNull RouteSessionInfo sessionInfo) {
+                @NonNull RoutingSessionInfo sessionInfo) {
 
             Client2Record client2Record = mSessionToClientMap.get(sessionInfo.getId());
             if (client2Record == null) {
@@ -1208,8 +1209,8 @@
             // TODO: Tell managers for the session release
         }
 
-        private void notifySessionCreated(Client2Record clientRecord, RouteSessionInfo sessionInfo,
-                int requestId) {
+        private void notifySessionCreated(Client2Record clientRecord,
+                RoutingSessionInfo sessionInfo, int requestId) {
             try {
                 clientRecord.mClient.notifySessionCreated(sessionInfo, requestId);
             } catch (RemoteException ex) {
@@ -1228,7 +1229,7 @@
         }
 
         private void notifySessionInfoChanged(Client2Record clientRecord,
-                RouteSessionInfo sessionInfo) {
+                RoutingSessionInfo sessionInfo) {
             try {
                 clientRecord.mClient.notifySessionInfoChanged(sessionInfo);
             } catch (RemoteException ex) {
@@ -1238,7 +1239,7 @@
         }
 
         private void notifySessionReleased(Client2Record clientRecord,
-                RouteSessionInfo sessionInfo) {
+                RoutingSessionInfo sessionInfo) {
             try {
                 clientRecord.mClient.notifySessionReleased(sessionInfo);
             } catch (RemoteException ex) {
@@ -1412,8 +1413,8 @@
                 try {
                     manager.notifyRouteSelected(clientRecord.mPackageName,
                             clientRecord.mSelectedRoute);
-                    manager.notifyRouteTypesChanged(clientRecord.mPackageName,
-                            clientRecord.mDiscoveryRequest.getRouteTypes());
+                    manager.notifyPreferredFeaturesChanged(clientRecord.mPackageName,
+                            clientRecord.mDiscoveryPreference.getPreferredFeatures());
                 } catch (RemoteException ex) {
                     Slog.w(TAG, "Failed to update client usage. Manager probably died.", ex);
                 }
@@ -1432,15 +1433,15 @@
         final class SessionCreationRequest {
             public final Client2Record mClientRecord;
             public final MediaRoute2Info mRoute;
-            public final String mRouteType;
+            public final String mRouteFeature;
             public final long mRequestId;
 
             SessionCreationRequest(@NonNull Client2Record clientRecord,
                     @NonNull MediaRoute2Info route,
-                    @NonNull String routeType, long requestId) {
+                    @NonNull String routeFeature, long requestId) {
                 mClientRecord = clientRecord;
                 mRoute = route;
-                mRouteType = routeType;
+                mRouteFeature = routeFeature;
                 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 c76555c..b7aa484 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -39,8 +39,8 @@
 import android.media.MediaRouterClientState;
 import android.media.RemoteDisplayState;
 import android.media.RemoteDisplayState.RemoteDisplayInfo;
-import android.media.RouteDiscoveryRequest;
-import android.media.RouteSessionInfo;
+import android.media.RouteDiscoveryPreference;
+import android.media.RoutingSessionInfo;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -520,7 +520,7 @@
     }
     // Binder call
     @Override
-    public void setDiscoveryRequest2(IMediaRouter2Client client, RouteDiscoveryRequest request) {
+    public void setDiscoveryRequest2(IMediaRouter2Client client, RouteDiscoveryPreference request) {
         mService2.setDiscoveryRequest2(client, request);
     }
 
@@ -552,7 +552,7 @@
 
     // Binder call
     @Override
-    public List<RouteSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
+    public List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
         return mService2.getActiveSessions(manager);
     }
 
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 0ea4e63..961d3d0 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -30,12 +30,12 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.R;
 
 import java.util.Collections;
+import java.util.List;
 
 /**
  * Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers.
@@ -55,6 +55,7 @@
     private final IAudioService mAudioService;
     private final Handler mHandler;
     private final Context mContext;
+    private final BluetoothRouteProvider mBtRouteProvider;
 
     private static ComponentName sComponentName = new ComponentName(
             SystemMediaRoute2Provider.class.getPackageName$(),
@@ -62,7 +63,7 @@
 
     //TODO: Clean up these when audio manager support multiple bt devices
     MediaRoute2Info mDefaultRoute;
-    MediaRoute2Info mBluetoothA2dpRoute;
+    @NonNull List<MediaRoute2Info> mBluetoothRoutes = Collections.EMPTY_LIST;
     final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
 
     final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
@@ -87,6 +88,10 @@
         mAudioService = IAudioService.Stub.asInterface(
                 ServiceManager.getService(Context.AUDIO_SERVICE));
 
+        mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> {
+            mBluetoothRoutes = routes;
+            publishRoutes();
+        });
         initializeRoutes();
     }
 
@@ -141,8 +146,8 @@
                         : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
                 .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
                 .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
-                .addRouteType(TYPE_LIVE_AUDIO)
-                .addRouteType(TYPE_LIVE_VIDEO)
+                .addFeature(TYPE_LIVE_AUDIO)
+                .addFeature(TYPE_LIVE_VIDEO)
                 .build();
 
         AudioRoutesInfo newAudioRoutes = null;
@@ -157,7 +162,15 @@
             updateAudioRoutes(newAudioRoutes);
         }
 
-        publishRoutes();
+        mBluetoothRoutes = mBtRouteProvider.getBluetoothRoutes();
+
+        MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
+        builder.addRoute(mDefaultRoute);
+        for (MediaRoute2Info route : mBluetoothRoutes) {
+            builder.addRoute(route);
+        }
+        setProviderState(builder.build(), Collections.emptyList());
+        mHandler.post(() -> notifyProviderState());
     }
 
     void updateAudioRoutes(AudioRoutesInfo newRoutes) {
@@ -181,25 +194,10 @@
                         : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
                 .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
                 .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
-                .addRouteType(TYPE_LIVE_AUDIO)
-                .addRouteType(TYPE_LIVE_VIDEO)
+                .addFeature(TYPE_LIVE_AUDIO)
+                .addFeature(TYPE_LIVE_VIDEO)
                 .build();
 
-        if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
-            mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
-            if (mCurAudioRoutesInfo.bluetoothName != null) {
-                //TODO: mark as bluetooth once MediaRoute2Info has device type
-                mBluetoothA2dpRoute = new MediaRoute2Info.Builder(BLUETOOTH_ROUTE_ID,
-                        mCurAudioRoutesInfo.bluetoothName)
-                        .setDescription(mContext.getResources().getText(
-                                R.string.bluetooth_a2dp_audio_route_name).toString())
-                        .addRouteType(TYPE_LIVE_AUDIO)
-                        .build();
-            } else {
-                mBluetoothA2dpRoute = null;
-            }
-        }
-
         publishRoutes();
     }
 
@@ -207,15 +205,13 @@
      * The first route should be the currently selected system route.
      * For example, if there are two system routes (BT and device speaker),
      * BT will be the first route in the list.
-     *
-     * TODO: Support multiple BT devices
      */
     void publishRoutes() {
         MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
-        if (mBluetoothA2dpRoute != null) {
-            builder.addRoute(mBluetoothA2dpRoute);
-        }
         builder.addRoute(mDefaultRoute);
+        for (MediaRoute2Info route : mBluetoothRoutes) {
+            builder.addRoute(route);
+        }
         setAndNotifyProviderState(builder.build(), Collections.emptyList());
     }
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 7f650ee..b24a938 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -18,8 +18,10 @@
 
 import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
 
+import android.annotation.NonNull;
 import android.net.Network;
 import android.net.NetworkTemplate;
+import android.net.netstats.provider.AbstractNetworkStatsProvider;
 import android.telephony.SubscriptionPlan;
 
 import java.util.Set;
@@ -126,4 +128,12 @@
      */
     public abstract void setMeteredRestrictedPackagesAsync(
             Set<String> packageNames, int userId);
+
+    /**
+     *  Notifies that any of the {@link AbstractNetworkStatsProvider} has reached its quota
+     *  which was set through {@link AbstractNetworkStatsProvider#setLimit(String, long)}.
+     *
+     * @param tag the human readable identifier of the custom network stats provider.
+     */
+    public abstract void onStatsProviderLimitReached(@NonNull String tag);
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 4a45730..d8a4655 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -75,6 +75,7 @@
 import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.net.netstats.provider.AbstractNetworkStatsProvider.QUOTA_UNLIMITED;
 import static android.os.Trace.TRACE_TAG_NETWORK;
 import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED;
 import static android.provider.Settings.Global.NETPOLICY_QUOTA_ENABLED;
@@ -391,6 +392,7 @@
     private static final int MSG_METERED_RESTRICTED_PACKAGES_CHANGED = 17;
     private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18;
     private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19;
+    private static final int MSG_STATS_PROVIDER_LIMIT_REACHED = 20;
 
     private static final int UID_MSG_STATE_CHANGED = 100;
     private static final int UID_MSG_GONE = 101;
@@ -4518,6 +4520,14 @@
                     mListeners.finishBroadcast();
                     return true;
                 }
+                case MSG_STATS_PROVIDER_LIMIT_REACHED: {
+                    mNetworkStats.forceUpdate();
+                    synchronized (mNetworkPoliciesSecondLock) {
+                        updateNetworkEnabledNL();
+                        updateNotificationsNL();
+                    }
+                    return true;
+                }
                 case MSG_LIMIT_REACHED: {
                     final String iface = (String) msg.obj;
 
@@ -4573,14 +4583,18 @@
                     return true;
                 }
                 case MSG_UPDATE_INTERFACE_QUOTA: {
-                    removeInterfaceQuota((String) msg.obj);
+                    final String iface = (String) msg.obj;
                     // int params need to be stitched back into a long
-                    setInterfaceQuota((String) msg.obj,
-                            ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL));
+                    final long quota = ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL);
+                    removeInterfaceQuota(iface);
+                    setInterfaceQuota(iface, quota);
+                    mNetworkStats.setStatsProviderLimit(iface, quota);
                     return true;
                 }
                 case MSG_REMOVE_INTERFACE_QUOTA: {
-                    removeInterfaceQuota((String) msg.obj);
+                    final String iface = (String) msg.obj;
+                    removeInterfaceQuota(iface);
+                    mNetworkStats.setStatsProviderLimit(iface, QUOTA_UNLIMITED);
                     return true;
                 }
                 case MSG_RESET_FIREWALL_RULES_BY_UID: {
@@ -5235,6 +5249,12 @@
             mHandler.obtainMessage(MSG_METERED_RESTRICTED_PACKAGES_CHANGED,
                     userId, 0, packageNames).sendToTarget();
         }
+
+        @Override
+        public void onStatsProviderLimitReached(@NonNull String tag) {
+            Log.v(TAG, "onStatsProviderLimitReached: " + tag);
+            mHandler.obtainMessage(MSG_STATS_PROVIDER_LIMIT_REACHED).sendToTarget();
+        }
     }
 
     private void setMeteredRestrictedPackagesInternal(Set<String> packageNames, int userId) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
index 4843ede..6d72cb5 100644
--- a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
@@ -16,6 +16,7 @@
 
 package com.android.server.net;
 
+import android.annotation.NonNull;
 import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 
@@ -34,4 +35,10 @@
 
     /** Force update of statistics. */
     public abstract void forceUpdate();
+
+    /**
+     * Set the quota limit to all registered custom network stats providers.
+     * Note that invocation of any interface will be sent to all providers.
+     */
+    public abstract void setStatsProviderLimit(@NonNull String iface, long quota);
 }
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 7a6f297..1dcff07 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
+import static android.Manifest.permission.UPDATE_DEVICE_STATS;
 import static android.content.Intent.ACTION_SHUTDOWN;
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.ACTION_USER_REMOVED;
@@ -71,6 +72,7 @@
 import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.app.usage.NetworkStatsManager;
@@ -97,6 +99,9 @@
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.net.TrafficStats;
+import android.net.netstats.provider.INetworkStatsProvider;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProviderCallback;
 import android.os.BestClock;
 import android.os.Binder;
 import android.os.DropBoxManager;
@@ -109,6 +114,7 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.PowerManager;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
@@ -176,7 +182,7 @@
      * This avoids firing the global alert too often on devices with high transfer speeds and
      * high quota.
      */
-    private static final int PERFORM_POLL_DELAY_MS = 1000;
+    private static final int DEFAULT_PERFORM_POLL_DELAY_MS = 1000;
 
     private static final String TAG_NETSTATS_ERROR = "netstats_error";
 
@@ -220,6 +226,7 @@
      */
     public interface NetworkStatsSettings {
         public long getPollInterval();
+        public long getPollDelay();
         public boolean getSampleEnabled();
         public boolean getAugmentEnabled();
 
@@ -248,6 +255,7 @@
     }
 
     private final Object mStatsLock = new Object();
+    private final Object mStatsProviderLock = new Object();
 
     /** Set of currently active ifaces. */
     @GuardedBy("mStatsLock")
@@ -272,6 +280,9 @@
     private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
             new DropBoxNonMonotonicObserver();
 
+    private final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList =
+            new RemoteCallbackList<>();
+
     @GuardedBy("mStatsLock")
     private NetworkStatsRecorder mDevRecorder;
     @GuardedBy("mStatsLock")
@@ -502,9 +513,9 @@
     }
 
     /**
-     * Register for a global alert that is delivered through
-     * {@link INetworkManagementEventObserver} once a threshold amount of data
-     * has been transferred.
+     * Register for a global alert that is delivered through {@link INetworkManagementEventObserver}
+     * or {@link NetworkStatsProviderCallback#onAlertReached()} once a threshold amount of data has
+     * been transferred.
      */
     private void registerGlobalAlert() {
         try {
@@ -514,6 +525,7 @@
         } catch (RemoteException e) {
             // ignored; service lives in system_server
         }
+        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setAlert(mGlobalAlertBytes));
     }
 
     @Override
@@ -803,8 +815,7 @@
     @Override
     public void incrementOperationCount(int uid, int tag, int operationCount) {
         if (Binder.getCallingUid() != uid) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.UPDATE_DEVICE_STATS, TAG);
+            mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
         }
 
         if (operationCount < 0) {
@@ -1095,7 +1106,7 @@
     /**
      * Observer that watches for {@link INetworkManagementService} alerts.
      */
-    private INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
+    private final INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
         @Override
         public void limitReached(String limitName, String iface) {
             // only someone like NMS should be calling us
@@ -1106,7 +1117,7 @@
                 // such a call pending; UID stats are handled during normal polling interval.
                 if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
                     mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
-                            PERFORM_POLL_DELAY_MS);
+                            mSettings.getPollDelay());
                 }
             }
         }
@@ -1252,6 +1263,14 @@
         xtSnapshot.combineAllValues(tetherSnapshot);
         devSnapshot.combineAllValues(tetherSnapshot);
 
+        // Snapshot for dev/xt stats from all custom stats providers. Counts per-interface data
+        // from stats providers that isn't already counted by dev and XT stats.
+        Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotStatsProvider");
+        final NetworkStats providersnapshot = getNetworkStatsFromProviders(STATS_PER_IFACE);
+        Trace.traceEnd(TRACE_TAG_NETWORK);
+        xtSnapshot.combineAllValues(providersnapshot);
+        devSnapshot.combineAllValues(providersnapshot);
+
         // For xt/dev, we pass a null VPN array because usage is aggregated by UID, so VPN traffic
         // can't be reattributed to responsible apps.
         Trace.traceBegin(TRACE_TAG_NETWORK, "recordDev");
@@ -1355,6 +1374,10 @@
             performSampleLocked();
         }
 
+        // request asynchronous stats update from all providers for next poll.
+        // TODO: request with a valid token.
+        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */));
+
         // finally, dispatch updated event to any listeners
         final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
         updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -1476,6 +1499,12 @@
         public void forceUpdate() {
             NetworkStatsService.this.forceUpdate();
         }
+
+        @Override
+        public void setStatsProviderLimit(@NonNull String iface, long quota) {
+            Slog.v(TAG, "setStatsProviderLimit(" + iface + "," + quota + ")");
+            invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setLimit(iface, quota));
+        }
     }
 
     @Override
@@ -1690,6 +1719,12 @@
             uidSnapshot.combineAllValues(vtStats);
         }
 
+        // get a stale copy of uid stats snapshot provided by providers.
+        final NetworkStats providerStats = getNetworkStatsFromProviders(STATS_PER_UID);
+        providerStats.filter(UID_ALL, ifaces, TAG_ALL);
+        mStatsFactory.apply464xlatAdjustments(uidSnapshot, providerStats, mUseBpfTrafficStats);
+        uidSnapshot.combineAllValues(providerStats);
+
         uidSnapshot.combineAllValues(mUidOperations);
 
         return uidSnapshot;
@@ -1726,6 +1761,152 @@
         }
     }
 
+    /**
+     * Registers a custom provider of {@link android.net.NetworkStats} to combine the network
+     * statistics that cannot be seen by the kernel to system. To unregister, invoke the
+     * {@code unregister()} of the returned callback.
+     *
+     * @param tag a human readable identifier of the custom network stats provider.
+     * @param provider the binder interface of
+     *                 {@link android.net.netstats.provider.AbstractNetworkStatsProvider} that
+     *                 needs to be registered to the system.
+     *
+     * @return a binder interface of
+     *         {@link android.net.netstats.provider.NetworkStatsProviderCallback}, which can be
+     *         used to report events to the system.
+     */
+    public @NonNull INetworkStatsProviderCallback registerNetworkStatsProvider(
+            @NonNull String tag, @NonNull INetworkStatsProvider provider) {
+        mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+        Objects.requireNonNull(provider, "provider is null");
+        Objects.requireNonNull(tag, "tag is null");
+        try {
+            NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl(
+                            tag, provider, mAlertObserver, mStatsProviderCbList);
+            mStatsProviderCbList.register(callback);
+            Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid="
+                    + getCallingUid() + "/" + getCallingPid());
+            return callback;
+        } catch (RemoteException e) {
+            Log.e(TAG, "registerNetworkStatsProvider failed", e);
+        }
+        return null;
+    }
+
+    // Collect stats from local cache of providers.
+    private @NonNull NetworkStats getNetworkStatsFromProviders(int how) {
+        final NetworkStats ret = new NetworkStats(0L, 0);
+        invokeForAllStatsProviderCallbacks((cb) -> ret.combineAllValues(cb.getCachedStats(how)));
+        return ret;
+    }
+
+    @FunctionalInterface
+    private interface ThrowingConsumer<S, T extends Throwable> {
+        void accept(S s) throws T;
+    }
+
+    private void invokeForAllStatsProviderCallbacks(
+            @NonNull ThrowingConsumer<NetworkStatsProviderCallbackImpl, RemoteException> task) {
+        synchronized (mStatsProviderCbList) {
+            final int length = mStatsProviderCbList.beginBroadcast();
+            try {
+                for (int i = 0; i < length; i++) {
+                    final NetworkStatsProviderCallbackImpl cb =
+                            mStatsProviderCbList.getBroadcastItem(i);
+                    try {
+                        task.accept(cb);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Fail to broadcast to provider: " + cb.mTag, e);
+                    }
+                }
+            } finally {
+                mStatsProviderCbList.finishBroadcast();
+            }
+        }
+    }
+
+    private static class NetworkStatsProviderCallbackImpl extends INetworkStatsProviderCallback.Stub
+            implements IBinder.DeathRecipient {
+        @NonNull final String mTag;
+        @NonNull private final Object mProviderStatsLock = new Object();
+        @NonNull final INetworkStatsProvider mProvider;
+        @NonNull final INetworkManagementEventObserver mAlertObserver;
+        @NonNull final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
+
+        @GuardedBy("mProviderStatsLock")
+        // STATS_PER_IFACE and STATS_PER_UID
+        private final NetworkStats mIfaceStats = new NetworkStats(0L, 0);
+        @GuardedBy("mProviderStatsLock")
+        private final NetworkStats mUidStats = new NetworkStats(0L, 0);
+
+        NetworkStatsProviderCallbackImpl(
+                @NonNull String tag, @NonNull INetworkStatsProvider provider,
+                @NonNull INetworkManagementEventObserver alertObserver,
+                @NonNull RemoteCallbackList<NetworkStatsProviderCallbackImpl> cbList)
+                throws RemoteException {
+            mTag = tag;
+            mProvider = provider;
+            mProvider.asBinder().linkToDeath(this, 0);
+            mAlertObserver = alertObserver;
+            mStatsProviderCbList = cbList;
+        }
+
+        @NonNull
+        public NetworkStats getCachedStats(int how) {
+            synchronized (mProviderStatsLock) {
+                NetworkStats stats;
+                switch (how) {
+                    case STATS_PER_IFACE:
+                        stats = mIfaceStats;
+                        break;
+                    case STATS_PER_UID:
+                        stats = mUidStats;
+                        break;
+                    default:
+                        throw new IllegalArgumentException("Invalid type: " + how);
+                }
+                // Return a defensive copy instead of local reference.
+                return stats.clone();
+            }
+        }
+
+        @Override
+        public void onStatsUpdated(int token, @Nullable NetworkStats ifaceStats,
+                @Nullable NetworkStats uidStats) {
+            // TODO: 1. Use token to map ifaces to correct NetworkIdentity.
+            //       2. Store the difference and store it directly to the recorder.
+            synchronized (mProviderStatsLock) {
+                if (ifaceStats != null) mIfaceStats.combineAllValues(ifaceStats);
+                if (uidStats != null) mUidStats.combineAllValues(uidStats);
+            }
+        }
+
+        @Override
+        public void onAlertReached() throws RemoteException {
+            mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */);
+        }
+
+        @Override
+        public void onLimitReached() {
+            Log.d(TAG, mTag + ": onLimitReached");
+            LocalServices.getService(NetworkPolicyManagerInternal.class)
+                    .onStatsProviderLimitReached(mTag);
+        }
+
+        @Override
+        public void binderDied() {
+            Log.d(TAG, mTag + ": binderDied");
+            mStatsProviderCbList.unregister(this);
+        }
+
+        @Override
+        public void unregister() {
+            Log.d(TAG, mTag + ": unregister");
+            mStatsProviderCbList.unregister(this);
+        }
+
+    }
+
     @VisibleForTesting
     static class HandlerCallback implements Handler.Callback {
         private final NetworkStatsService mService;
@@ -1815,6 +1996,10 @@
             return getGlobalLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS);
         }
         @Override
+        public long getPollDelay() {
+            return DEFAULT_PERFORM_POLL_DELAY_MS;
+        }
+        @Override
         public long getGlobalAlertBytes(long def) {
             return getGlobalLong(NETSTATS_GLOBAL_ALERT_BYTES, def);
         }
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index b782ca96..3c31f6a 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -83,6 +83,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -925,7 +926,7 @@
     /**
      * Updates the target packages' set of enabled overlays in PackageManager.
      */
-    private void updateOverlayPaths(int userId, List<String> targetPackageNames) {
+    private ArrayList<String> updateOverlayPaths(int userId, List<String> targetPackageNames) {
         try {
             traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames);
             if (DEBUG) {
@@ -955,6 +956,7 @@
                 }
             }
 
+            final HashSet<String> updatedPackages = new HashSet<>();
             final int n = targetPackageNames.size();
             for (int i = 0; i < n; i++) {
                 final String targetPackageName = targetPackageNames.get(i);
@@ -965,11 +967,13 @@
                 }
 
                 if (!pm.setEnabledOverlayPackages(
-                        userId, targetPackageName, pendingChanges.get(targetPackageName))) {
+                        userId, targetPackageName, pendingChanges.get(targetPackageName),
+                        updatedPackages)) {
                     Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
                             targetPackageName, userId));
                 }
             }
+            return new ArrayList<>(updatedPackages);
         } finally {
             traceEnd(TRACE_TAG_RRO);
         }
@@ -980,10 +984,10 @@
     }
 
     private void updateAssets(final int userId, List<String> targetPackageNames) {
-        updateOverlayPaths(userId, targetPackageNames);
         final IActivityManager am = ActivityManager.getService();
         try {
-            am.scheduleApplicationInfoChanged(targetPackageNames, userId);
+            final ArrayList<String> updatedPaths = updateOverlayPaths(userId, targetPackageNames);
+            am.scheduleApplicationInfoChanged(updatedPaths, userId);
         } catch (RemoteException e) {
             // Intentionally left empty.
         }
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 019c952..9623542 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -685,7 +685,7 @@
         // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
         if (targetPackage != null && overlayPackage != null
                 && !("android".equals(targetPackageName)
-                        && overlayPackage.isStaticOverlayPackage())) {
+                    && overlayPackage.isStaticOverlayPackage())) {
             mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
         }
 
@@ -703,9 +703,9 @@
         if (currentState != newState) {
             if (DEBUG) {
                 Slog.d(TAG, String.format("%s:%d: %s -> %s",
-                            overlayPackageName, userId,
-                            OverlayInfo.stateToString(currentState),
-                            OverlayInfo.stateToString(newState)));
+                        overlayPackageName, userId,
+                        OverlayInfo.stateToString(currentState),
+                        OverlayInfo.stateToString(newState)));
             }
             modified |= mSettings.setState(overlayPackageName, userId, newState);
         }
diff --git a/services/core/java/com/android/server/om/TEST_MAPPING b/services/core/java/com/android/server/om/TEST_MAPPING
new file mode 100644
index 0000000..52163a0
--- /dev/null
+++ b/services/core/java/com/android/server/om/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.om."
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index a009183..2807909 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -55,7 +55,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -376,6 +375,33 @@
             }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
         }
 
+        private void populatePackageNameToApexModuleNameIfNeeded() {
+            synchronized (mLock) {
+                if (mPackageNameToApexModuleName != null) {
+                    return;
+                }
+                try {
+                    mPackageNameToApexModuleName = new ArrayMap<>();
+                    final ApexInfo[] allPkgs = mApexService.getAllPackages();
+                    for (int i = 0; i < allPkgs.length; i++) {
+                        ApexInfo ai = allPkgs[i];
+                        PackageParser.PackageLite pkgLite;
+                        try {
+                            File apexFile = new File(ai.modulePath);
+                            pkgLite = PackageParser.parsePackageLite(apexFile, 0);
+                        } catch (PackageParser.PackageParserException pe) {
+                            throw new IllegalStateException("Unable to parse: "
+                                    + ai.modulePath, pe);
+                        }
+                        mPackageNameToApexModuleName.put(pkgLite.packageName, ai.moduleName);
+                    }
+                } catch (RemoteException re) {
+                    Slog.e(TAG, "Unable to retrieve packages from apexservice: ", re);
+                    throw new RuntimeException(re);
+                }
+            }
+        }
+
         private void populateAllPackagesCacheIfNeeded() {
             synchronized (mLock) {
                 if (mAllPackagesCache != null) {
@@ -383,7 +409,6 @@
                 }
                 try {
                     mAllPackagesCache = new ArrayList<>();
-                    mPackageNameToApexModuleName = new HashMap<>();
                     HashSet<String> activePackagesSet = new HashSet<>();
                     HashSet<String> factoryPackagesSet = new HashSet<>();
                     final ApexInfo[] allPkgs = mApexService.getAllPackages();
@@ -409,7 +434,6 @@
                         final PackageInfo packageInfo =
                                 PackageParser.generatePackageInfo(pkg, ai, flags);
                         mAllPackagesCache.add(packageInfo);
-                        mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName);
                         if (ai.isActive) {
                             if (activePackagesSet.contains(packageInfo.packageName)) {
                                 throw new IllegalStateException(
@@ -612,8 +636,7 @@
 
         @Override
         List<String> getApksInApex(String apexPackageName) {
-            // TODO(b/142712057): Avoid calling populateAllPackagesCacheIfNeeded during boot.
-            populateAllPackagesCacheIfNeeded();
+            populatePackageNameToApexModuleNameIfNeeded();
             synchronized (mLock) {
                 String moduleName = mPackageNameToApexModuleName.get(apexPackageName);
                 if (moduleName == null) {
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index c4bcf80..3e76096 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -449,6 +449,7 @@
             }
             final PackageSetting callingPkgSetting;
             final ArraySet<PackageSetting> callingSharedPkgSettings;
+            Trace.beginSection("callingSetting instanceof");
             if (callingSetting instanceof PackageSetting) {
                 callingPkgSetting = (PackageSetting) callingSetting;
                 callingSharedPkgSettings = null;
@@ -456,6 +457,7 @@
                 callingPkgSetting = null;
                 callingSharedPkgSettings = ((SharedUserSetting) callingSetting).packages;
             }
+            Trace.endSection();
 
             if (callingPkgSetting != null) {
                 if (!mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) {
@@ -485,6 +487,7 @@
                 return true;
             }
             final String targetName = targetPkg.getPackageName();
+            Trace.beginSection("getAppId");
             final int callingAppId;
             if (callingPkgSetting != null) {
                 callingAppId = callingPkgSetting.appId;
@@ -492,6 +495,7 @@
                 callingAppId = callingSharedPkgSettings.valueAt(0).appId; // all should be the same
             }
             final int targetAppId = targetPkgSetting.appId;
+            Trace.endSection();
             if (callingAppId == targetAppId) {
                 if (DEBUG_LOGGING) {
                     log(callingSetting, targetPkgSetting, "same app id");
@@ -499,38 +503,64 @@
                 return false;
             }
 
-            if (callingSetting.getPermissionsState().hasPermission(
-                    Manifest.permission.QUERY_ALL_PACKAGES, UserHandle.getUserId(callingUid))) {
-                if (DEBUG_LOGGING) {
-                    log(callingSetting, targetPkgSetting, "has query-all permission");
+            try {
+                Trace.beginSection("hasPermission");
+                if (callingSetting.getPermissionsState().hasPermission(
+                        Manifest.permission.QUERY_ALL_PACKAGES, UserHandle.getUserId(callingUid))) {
+                    if (DEBUG_LOGGING) {
+                        log(callingSetting, targetPkgSetting, "has query-all permission");
+                    }
+                    return false;
                 }
-                return false;
+            } finally {
+                Trace.endSection();
             }
-            if (mForceQueryable.contains(targetAppId)) {
-                if (DEBUG_LOGGING) {
-                    log(callingSetting, targetPkgSetting, "force queryable");
+            try {
+                Trace.beginSection("mForceQueryable");
+                if (mForceQueryable.contains(targetAppId)) {
+                    if (DEBUG_LOGGING) {
+                        log(callingSetting, targetPkgSetting, "force queryable");
+                    }
+                    return false;
                 }
-                return false;
+            } finally {
+                Trace.endSection();
             }
-            if (mQueriesViaPackage.contains(callingAppId, targetAppId)) {
-                // the calling package has explicitly declared the target package; allow
-                if (DEBUG_LOGGING) {
-                    log(callingSetting, targetPkgSetting, "queries package");
+            try {
+                Trace.beginSection("mQueriesViaPackage");
+                if (mQueriesViaPackage.contains(callingAppId, targetAppId)) {
+                    // the calling package has explicitly declared the target package; allow
+                    if (DEBUG_LOGGING) {
+                        log(callingSetting, targetPkgSetting, "queries package");
+                    }
+                    return false;
                 }
-                return false;
-            } else if (mQueriesViaIntent.contains(callingAppId, targetAppId)) {
-                if (DEBUG_LOGGING) {
-                    log(callingSetting, targetPkgSetting, "queries intent");
+            } finally {
+                Trace.endSection();
+            }
+            try {
+                Trace.beginSection("mQueriesViaIntent");
+                if (mQueriesViaIntent.contains(callingAppId, targetAppId)) {
+                    if (DEBUG_LOGGING) {
+                        log(callingSetting, targetPkgSetting, "queries intent");
+                    }
+                    return false;
                 }
-                return false;
+            } finally {
+                Trace.endSection();
             }
 
-            final int targetUid = UserHandle.getUid(userId, targetAppId);
-            if (mImplicitlyQueryable.contains(callingUid, targetUid)) {
-                if (DEBUG_LOGGING) {
-                    log(callingSetting, targetPkgSetting, "implicitly queryable for user");
+            try {
+                Trace.beginSection("mImplicitlyQueryable");
+                final int targetUid = UserHandle.getUid(userId, targetAppId);
+                if (mImplicitlyQueryable.contains(callingUid, targetUid)) {
+                    if (DEBUG_LOGGING) {
+                        log(callingSetting, targetPkgSetting, "implicitly queryable for user");
+                    }
+                    return false;
                 }
-                return false;
+            } finally {
+                Trace.endSection();
             }
             if (callingPkgSetting != null) {
                 if (callingPkgInstruments(callingPkgSetting, targetPkgSetting, targetName)) {
@@ -576,17 +606,22 @@
     private static boolean callingPkgInstruments(PackageSetting callingPkgSetting,
             PackageSetting targetPkgSetting,
             String targetName) {
-        final List<ComponentParseUtils.ParsedInstrumentation> inst =
-                callingPkgSetting.pkg.getInstrumentations();
-        for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) {
-            if (Objects.equals(inst.get(i).getTargetPackage(), targetName)) {
-                if (DEBUG_LOGGING) {
-                    log(callingPkgSetting, targetPkgSetting, "instrumentation");
+        try {
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingPkgInstruments");
+            final List<ComponentParseUtils.ParsedInstrumentation> inst =
+                    callingPkgSetting.pkg.getInstrumentations();
+            for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) {
+                if (Objects.equals(inst.get(i).getTargetPackage(), targetName)) {
+                    if (DEBUG_LOGGING) {
+                        log(callingPkgSetting, targetPkgSetting, "instrumentation");
+                    }
+                    return true;
                 }
-                return true;
             }
+            return false;
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
-        return false;
     }
 
     private static void log(SettingBase callingPkgSetting, PackageSetting targetPkgSetting,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 17870eb..dad32cd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11602,6 +11602,23 @@
             return false;
         }
         SharedLibraryInfo libraryInfo = versionedLib.valueAt(libIdx);
+
+        // Remove the shared library overlays from its dependent packages.
+        for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
+            final List<VersionedPackage> dependents = getPackagesUsingSharedLibraryLPr(
+                    libraryInfo, 0, currentUserId);
+            if (dependents == null) {
+                continue;
+            }
+            for (VersionedPackage dependentPackage : dependents) {
+                final PackageSetting ps = mSettings.mPackages.get(
+                        dependentPackage.getPackageName());
+                if (ps != null) {
+                    ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId);
+                }
+            }
+        }
+
         versionedLib.remove(version);
         if (versionedLib.size() <= 0) {
             mSharedLibraries.remove(name);
@@ -15210,6 +15227,29 @@
                         // upcoming call to mSettings.writeLPr().
                     }
                 }
+
+                // Retrieve the overlays for shared libraries of the package.
+                if (pkg.getUsesLibraryInfos() != null) {
+                    for (SharedLibraryInfo sharedLib : pkg.getUsesLibraryInfos()) {
+                        for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
+                            if (!sharedLib.isDynamic()) {
+                                // TODO(146804378): Support overlaying static shared libraries
+                                continue;
+                            }
+                            final PackageSetting libPs = mSettings.mPackages.get(
+                                    sharedLib.getPackageName());
+                            if (libPs == null) {
+                                continue;
+                            }
+                            final String[] overlayPaths = libPs.getOverlayPaths(currentUserId);
+                            if (overlayPaths != null) {
+                                ps.setOverlayPathsForLibrary(sharedLib.getName(),
+                                        Arrays.asList(overlayPaths), currentUserId);
+                            }
+                        }
+                    }
+                }
+
                 // It's implied that when a user requests installation, they want the app to be
                 // installed and enabled.
                 if (userId != UserHandle.USER_ALL) {
@@ -23249,9 +23289,11 @@
 
         @Override
         public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
-                @Nullable List<String> overlayPackageNames) {
+                @Nullable List<String> overlayPackageNames,
+                @NonNull Collection<String> outUpdatedPackageNames) {
             synchronized (mLock) {
-                if (targetPackageName == null || mPackages.get(targetPackageName) == null) {
+                final AndroidPackage targetPkg = mPackages.get(targetPackageName);
+                if (targetPackageName == null || targetPkg == null) {
                     Slog.e(TAG, "failed to find package " + targetPackageName);
                     return false;
                 }
@@ -23270,8 +23312,41 @@
                     }
                 }
 
+                ArraySet<String> updatedPackageNames = null;
+                if (targetPkg.getLibraryNames() != null) {
+                    // Set the overlay paths for dependencies of the shared library.
+                    updatedPackageNames = new ArraySet<>();
+                    for (String libName : targetPkg.getLibraryNames()) {
+                        final SharedLibraryInfo info = getSharedLibraryInfoLPr(libName,
+                                SharedLibraryInfo.VERSION_UNDEFINED);
+                        if (info == null) {
+                            continue;
+                        }
+                        final List<VersionedPackage> dependents = getPackagesUsingSharedLibraryLPr(
+                                info, 0, userId);
+                        if (dependents == null) {
+                            continue;
+                        }
+                        for (VersionedPackage dependent : dependents) {
+                            final PackageSetting ps = mSettings.mPackages.get(
+                                    dependent.getPackageName());
+                            if (ps == null) {
+                                continue;
+                            }
+                            ps.setOverlayPathsForLibrary(libName, overlayPaths, userId);
+                            updatedPackageNames.add(dependent.getPackageName());
+                        }
+                    }
+                }
+
                 final PackageSetting ps = mSettings.mPackages.get(targetPackageName);
                 ps.setOverlayPaths(overlayPaths, userId);
+
+                outUpdatedPackageNames.add(targetPackageName);
+                if (updatedPackageNames != null) {
+                    outUpdatedPackageNames.addAll(updatedPackageNames);
+                }
+
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 0c0b93b..f1ac0af 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -41,6 +41,7 @@
 import java.io.File;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -312,12 +313,21 @@
     }
 
     void setOverlayPaths(List<String> overlayPaths, int userId) {
-        modifyUserState(userId).overlayPaths = overlayPaths == null ? null :
-            overlayPaths.toArray(new String[overlayPaths.size()]);
+        modifyUserState(userId).setOverlayPaths(overlayPaths == null ? null :
+            overlayPaths.toArray(new String[overlayPaths.size()]));
     }
 
     String[] getOverlayPaths(int userId) {
-        return readUserState(userId).overlayPaths;
+        return readUserState(userId).getOverlayPaths();
+    }
+
+    void setOverlayPathsForLibrary(String libName, List<String> overlayPaths, int userId) {
+        modifyUserState(userId).setSharedLibraryOverlayPaths(libName,
+                overlayPaths == null ? null : overlayPaths.toArray(new String[0]));
+    }
+
+    Map<String, String[]> getOverlayPathsForLibrary(int userId) {
+        return readUserState(userId).getSharedLibraryOverlayPaths();
     }
 
     /** Only use for testing. Do NOT use in production code. */
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index ec84b51..3e665c3 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4742,6 +4742,22 @@
                 }
             }
 
+            Map<String, String[]> sharedLibraryOverlayPaths =
+                    ps.getOverlayPathsForLibrary(user.id);
+            if (sharedLibraryOverlayPaths != null) {
+                for (Map.Entry<String, String[]> libOverlayPaths :
+                        sharedLibraryOverlayPaths.entrySet()) {
+                    if (libOverlayPaths.getValue() == null) {
+                        continue;
+                    }
+                    pw.print(prefix); pw.print("  ");
+                    pw.print(libOverlayPaths.getKey()); pw.println(" overlay paths:");
+                    for (String path : libOverlayPaths.getValue()) {
+                        pw.print(prefix); pw.print("    "); pw.println(path);
+                    }
+                }
+            }
+
             String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
             if (lastDisabledAppCaller != null) {
                 pw.print(prefix); pw.print("    lastDisabledCaller: ");
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 6d6ec25..46893b2 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -592,12 +592,6 @@
                 getDefaultSystemHandlerActivityPackageForCategory(Intent.CATEGORY_APP_MAPS, userId),
                 userId, ALWAYS_LOCATION_PERMISSIONS);
 
-        // Gallery
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackageForCategory(
-                        Intent.CATEGORY_APP_GALLERY, userId),
-                userId, STORAGE_PERMISSIONS);
-
         // Email
         grantPermissionsToSystemPackage(
                 getDefaultSystemHandlerActivityPackageForCategory(
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index d8c1966..d468cd9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -4571,8 +4571,8 @@
 
         @Override
         public void onNewUserCreated(int userId) {
+            mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId);
             synchronized (mLock) {
-                mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId);
                 // NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
                 PermissionManagerService.this.updateAllPermissions(
                         StorageManager.UUID_PRIVATE_INTERNAL, true, mDefaultPermissionCallback);
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index 8385f40..a641f06 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -24,6 +24,7 @@
 import android.hardware.soundtrigger.V2_3.Properties;
 import android.media.audio.common.AudioConfig;
 import android.media.audio.common.AudioOffloadInfo;
+import android.media.soundtrigger_middleware.AudioCapabilities;
 import android.media.soundtrigger_middleware.ConfidenceLevel;
 import android.media.soundtrigger_middleware.ModelParameter;
 import android.media.soundtrigger_middleware.ModelParameterRange;
@@ -74,6 +75,8 @@
             @NonNull Properties hidlProperties) {
         SoundTriggerModuleProperties aidlProperties = hidl2aidlProperties(hidlProperties.base);
         aidlProperties.supportedModelArch = hidlProperties.supportedModelArch;
+        aidlProperties.audioCapabilities =
+                hidl2aidlAudioCapabilities(hidlProperties.audioCapabilities);
         return aidlProperties;
     }
 
@@ -209,16 +212,17 @@
         return hidlModel;
     }
 
-    static @NonNull
-    ISoundTriggerHw.RecognitionConfig aidl2hidlRecognitionConfig(
+    static @NonNull android.hardware.soundtrigger.V2_3.RecognitionConfig aidl2hidlRecognitionConfig(
             @NonNull RecognitionConfig aidlConfig) {
-        ISoundTriggerHw.RecognitionConfig hidlConfig = new ISoundTriggerHw.RecognitionConfig();
-        hidlConfig.header.captureRequested = aidlConfig.captureRequested;
+        android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
+                new android.hardware.soundtrigger.V2_3.RecognitionConfig();
+        hidlConfig.base.header.captureRequested = aidlConfig.captureRequested;
         for (PhraseRecognitionExtra aidlPhraseExtra : aidlConfig.phraseRecognitionExtras) {
-            hidlConfig.header.phrases.add(aidl2hidlPhraseRecognitionExtra(aidlPhraseExtra));
+            hidlConfig.base.header.phrases.add(aidl2hidlPhraseRecognitionExtra(aidlPhraseExtra));
         }
-        hidlConfig.data = HidlMemoryUtil.byteArrayToHidlMemory(aidlConfig.data,
+        hidlConfig.base.data = HidlMemoryUtil.byteArrayToHidlMemory(aidlConfig.data,
                 "SoundTrigger RecognitionConfig");
+        hidlConfig.audioCapabilities = aidlConfig.audioCapabilities;
         return hidlConfig;
     }
 
@@ -395,4 +399,17 @@
                 return android.hardware.soundtrigger.V2_3.ModelParameter.INVALID;
         }
     }
+
+    static int hidl2aidlAudioCapabilities(int hidlCapabilities) {
+        int aidlCapabilities = 0;
+        if ((hidlCapabilities
+                & android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION) != 0) {
+            aidlCapabilities |= AudioCapabilities.ECHO_CANCELLATION;
+        }
+        if ((hidlCapabilities
+                & android.hardware.soundtrigger.V2_3.AudioCapabilities.NOISE_SUPPRESSION) != 0) {
+            aidlCapabilities |= AudioCapabilities.NOISE_SUPPRESSION;
+        }
+        return aidlCapabilities;
+    }
 }
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
index dbf91a9..a42d292 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
@@ -66,12 +66,17 @@
         return model_2_0;
     }
 
-    static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig convertRecognitionConfig_2_1_to_2_0(
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config) {
+    static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig convertRecognitionConfig_2_3_to_2_1(
+            android.hardware.soundtrigger.V2_3.RecognitionConfig config) {
+        return config.base;
+    }
+
+    static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig convertRecognitionConfig_2_3_to_2_0(
+            android.hardware.soundtrigger.V2_3.RecognitionConfig config) {
         android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config_2_0 =
-                config.header;
+                config.base.header;
         // Note: this mutates the input!
-        config_2_0.data = HidlMemoryUtil.hidlMemoryToByteList(config.data);
+        config_2_0.data = HidlMemoryUtil.hidlMemoryToByteList(config.base.data);
         return config_2_0;
     }
 
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
index 2f024a5..8b434bd 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
@@ -92,12 +92,12 @@
     void stopAllRecognitions();
 
     /**
-     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#startRecognition_2_1(int,
-     * android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig,
+     * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#startRecognition_2_3(int,
+     * android.hardware.soundtrigger.V2_3.RecognitionConfig,
      * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int)
      */
     void startRecognition(int modelHandle,
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config,
+            android.hardware.soundtrigger.V2_3.RecognitionConfig config,
             SoundTriggerHw2Compat.Callback callback, int cookie);
 
     /**
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index 3354c56..2f087f4 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -217,16 +217,15 @@
 
     @Override
     public void startRecognition(int modelHandle,
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config,
+            android.hardware.soundtrigger.V2_3.RecognitionConfig config,
             Callback callback, int cookie) {
         try {
             try {
-                int retval = as2_1().startRecognition_2_1(modelHandle, config,
-                        new SoundTriggerCallback(callback), cookie);
-                handleHalStatus(retval, "startRecognition_2_1");
+                int retval = as2_3().startRecognition_2_3(modelHandle, config);
+                handleHalStatus(retval, "startRecognition_2_3");
             } catch (NotSupported e) {
                 // Fall-back to the 2.0 version:
-                startRecognition_2_0(modelHandle, config, callback, cookie);
+                startRecognition_2_1(modelHandle, config, callback, cookie);
             }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
@@ -369,13 +368,31 @@
         return handle.get();
     }
 
+    private void startRecognition_2_1(int modelHandle,
+            android.hardware.soundtrigger.V2_3.RecognitionConfig config,
+            Callback callback, int cookie) {
+        try {
+            try {
+                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config_2_1 =
+                        Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_1(config);
+                int retval = as2_1().startRecognition_2_1(modelHandle, config_2_1,
+                        new SoundTriggerCallback(callback), cookie);
+                handleHalStatus(retval, "startRecognition_2_1");
+            } catch (NotSupported e) {
+                // Fall-back to the 2.0 version:
+                startRecognition_2_0(modelHandle, config, callback, cookie);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
     private void startRecognition_2_0(int modelHandle,
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config,
+            android.hardware.soundtrigger.V2_3.RecognitionConfig config,
             Callback callback, int cookie)
             throws RemoteException {
-
         android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config_2_0 =
-                Hw2CompatUtil.convertRecognitionConfig_2_1_to_2_0(config);
+                Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_0(config);
         int retval = as2_0().startRecognition(modelHandle, config_2_0,
                 new SoundTriggerCallback(callback), cookie);
         handleHalStatus(retval, "startRecognition");
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 81789e1..f024ede 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -401,10 +401,10 @@
                     notifyAbort();
                     return;
                 }
-                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig hidlConfig =
+                android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
                         ConversionUtil.aidl2hidlRecognitionConfig(config);
-                hidlConfig.header.captureDevice = mSession.mDeviceHandle;
-                hidlConfig.header.captureHandle = mSession.mIoHandle;
+                hidlConfig.base.header.captureDevice = mSession.mDeviceHandle;
+                hidlConfig.base.header.captureHandle = mSession.mIoHandle;
                 mHalService.startRecognition(mHandle, hidlConfig, this, 0);
                 setState(ModelState.ACTIVE);
             }
diff --git a/services/core/java/com/android/server/stats/OWNERS b/services/core/java/com/android/server/stats/OWNERS
index 8d7f882..fc7fd22 100644
--- a/services/core/java/com/android/server/stats/OWNERS
+++ b/services/core/java/com/android/server/stats/OWNERS
@@ -1,9 +1,8 @@
-bookatz@google.com
-cjyu@google.com
-dwchen@google.com
+jeffreyhuang@google.com
 joeo@google.com
+muhammadq@google.com
+ruchirr@google.com
 singhtejinder@google.com
-stlafon@google.com
+tsaichristine@google.com
 yaochen@google.com
-yanglu@google.com
-yro@google.com
\ No newline at end of file
+yro@google.com
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index f661b5e..c964795 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -21,7 +21,6 @@
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.PhoneTimeSuggestion;
-import android.content.Intent;
 import android.os.TimestampedValue;
 
 import java.io.PrintWriter;
@@ -73,9 +72,6 @@
 
         /** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */
         void releaseWakeLock();
-
-        /** Send the supplied intent as a stick broadcast. */
-        void sendStickyBroadcast(@NonNull Intent intent);
     }
 
     /** Initialize the strategy. */
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
index 42d59d5..9b89d94 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
@@ -20,11 +20,9 @@
 import android.app.AlarmManager;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.Intent;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
 
@@ -112,11 +110,6 @@
         mWakeLock.release();
     }
 
-    @Override
-    public void sendStickyBroadcast(@NonNull Intent intent) {
-        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
-    }
-
     private void checkWakeLockHeld() {
         if (!mWakeLock.isHeld()) {
             Slog.wtf(TAG, "WakeLock " + mWakeLock + " not held");
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index da848d8..e95fc4a 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -23,9 +23,7 @@
 import android.app.timedetector.ManualTimeSuggestion;
 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;
 
@@ -535,17 +533,6 @@
         } else {
             mLastAutoSystemClockTimeSet = null;
         }
-
-        // Historically, Android has sent a TelephonyManager.ACTION_NETWORK_SET_TIME broadcast only
-        // when setting the time using NITZ.
-        if (origin == ORIGIN_PHONE) {
-            // Send a broadcast that telephony code used to send after setting the clock.
-            // TODO Remove this broadcast as soon as there are no remaining listeners.
-            Intent intent = new Intent(TelephonyManager.ACTION_NETWORK_SET_TIME);
-            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-            intent.putExtra("time", newSystemClockMillis);
-            mCallback.sendStickyBroadcast(intent);
-        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 5b58199..eb7c5ca 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -127,6 +127,9 @@
     // A map from user id to UserState.
     private final SparseArray<UserState> mUserStates = new SparseArray<>();
 
+    // A map from session id to session state saved in userstate
+    private final Map<String, SessionState> mSessionIdToSessionStateMap = new HashMap<>();
+
     private final WatchLogHandler mWatchLogHandler;
 
     public TvInputManagerService(Context context) {
@@ -632,7 +635,8 @@
         UserState userState = getOrCreateUserStateLocked(userId);
         SessionState sessionState = userState.sessionStateMap.get(sessionToken);
         if (DEBUG) {
-            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.inputId + ")");
+            Slog.d(TAG, "createSessionInternalLocked(inputId="
+                    + sessionState.inputId + ", sessionId=" + sessionState.sessionId + ")");
         }
         InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
 
@@ -643,9 +647,11 @@
         // Create a session. When failed, send a null token immediately.
         try {
             if (sessionState.isRecordingSession) {
-                service.createRecordingSession(callback, sessionState.inputId);
+                service.createRecordingSession(
+                        callback, sessionState.inputId, sessionState.sessionId);
             } else {
-                service.createSession(channels[1], callback, sessionState.inputId);
+                service.createSession(
+                        channels[1], callback, sessionState.inputId, sessionState.sessionId);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "error in createSession", e);
@@ -715,6 +721,8 @@
             }
         }
 
+        mSessionIdToSessionStateMap.remove(sessionState.sessionId);
+
         ServiceState serviceState = userState.serviceStateMap.get(sessionState.componentName);
         if (serviceState != null) {
             serviceState.sessionTokens.remove(sessionToken);
@@ -1156,9 +1164,11 @@
         public void createSession(final ITvInputClient client, final String inputId,
                 boolean isRecordingSession, int seq, int userId) {
             final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
                     userId, "createSession");
             final long identity = Binder.clearCallingIdentity();
+            StringBuilder sessionId = new StringBuilder();
             try {
                 synchronized (mLock) {
                     if (userId != mCurrentUserId && !isRecordingSession) {
@@ -1187,15 +1197,21 @@
                         return;
                     }
 
+                    // Create a unique session id with pid, uid and resolved user id
+                    sessionId.append(callingUid).append(callingPid).append(resolvedUserId);
+
                     // Create a new session token and a session state.
                     IBinder sessionToken = new Binder();
                     SessionState sessionState = new SessionState(sessionToken, info.getId(),
                             info.getComponent(), isRecordingSession, client, seq, callingUid,
-                            resolvedUserId);
+                            callingPid, resolvedUserId, sessionId.toString());
 
                     // Add them to the global session state map of the current user.
                     userState.sessionStateMap.put(sessionToken, sessionState);
 
+                    // Map the session id to the sessionStateMap in the user state
+                    mSessionIdToSessionStateMap.put(sessionId.toString(), sessionState);
+
                     // Also, add them to the session state map of the current service.
                     serviceState.sessionTokens.add(sessionToken);
 
@@ -2003,6 +2019,43 @@
         }
 
         @Override
+        public int getClientPid(String sessionId) {
+            ensureTunerResourceAccessPermission();
+            final long identity = Binder.clearCallingIdentity();
+
+            int clientPid = TvInputManager.UNKNOWN_CLIENT_PID;
+            try {
+                synchronized (mLock) {
+                    try {
+                        clientPid = getClientPidLocked(sessionId);
+                    } catch (ClientPidNotFoundException e) {
+                        Slog.e(TAG, "error in getClientPid", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            return clientPid;
+        }
+
+        private int getClientPidLocked(String sessionId)
+                throws IllegalStateException {
+            if (mSessionIdToSessionStateMap.get(sessionId) == null) {
+                throw new IllegalStateException("Client Pid not found with sessionId "
+                        + sessionId);
+            }
+            return mSessionIdToSessionStateMap.get(sessionId).callingPid;
+        }
+
+        private void ensureTunerResourceAccessPermission() {
+            if (mContext.checkCallingPermission(
+                    android.Manifest.permission.TUNER_RESOURCE_ACCESS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Requires TUNER_RESOURCE_ACCESS permission");
+            }
+        }
+
+        @Override
         @SuppressWarnings("resource")
         protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
@@ -2094,9 +2147,11 @@
 
                         pw.increaseIndent();
                         pw.println("inputId: " + session.inputId);
+                        pw.println("sessionId: " + session.sessionId);
                         pw.println("client: " + session.client);
                         pw.println("seq: " + session.seq);
                         pw.println("callingUid: " + session.callingUid);
+                        pw.println("callingPid: " + session.callingPid);
                         pw.println("userId: " + session.userId);
                         pw.println("sessionToken: " + session.sessionToken);
                         pw.println("session: " + session.session);
@@ -2226,11 +2281,13 @@
 
     private final class SessionState implements IBinder.DeathRecipient {
         private final String inputId;
+        private final String sessionId;
         private final ComponentName componentName;
         private final boolean isRecordingSession;
         private final ITvInputClient client;
         private final int seq;
         private final int callingUid;
+        private final int callingPid;
         private final int userId;
         private final IBinder sessionToken;
         private ITvInputSession session;
@@ -2240,7 +2297,7 @@
 
         private SessionState(IBinder sessionToken, String inputId, ComponentName componentName,
                 boolean isRecordingSession, ITvInputClient client, int seq, int callingUid,
-                int userId) {
+                int callingPid, int userId, String sessionId) {
             this.sessionToken = sessionToken;
             this.inputId = inputId;
             this.componentName = componentName;
@@ -2248,7 +2305,9 @@
             this.client = client;
             this.seq = seq;
             this.callingUid = callingUid;
+            this.callingPid = callingPid;
             this.userId = userId;
+            this.sessionId = sessionId;
         }
 
         @Override
@@ -2962,4 +3021,10 @@
             super(name);
         }
     }
+
+    private static class ClientPidNotFoundException extends IllegalArgumentException {
+        public ClientPidNotFoundException(String name) {
+            super(name);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 26d76a8d..320be2d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5934,7 +5934,11 @@
             mAnimationBoundsLayer = createAnimationBoundsLayer(t);
 
             // Crop to stack bounds.
-            t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
+            if (!WindowManagerService.sHierarchicalAnimations) {
+                // For Hierarchical animation, we don't need to set window crop since the leash
+                // surface size has already same as the animating container.
+                t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
+            }
             t.setLayer(mAnimationBoundsLayer, layer);
 
             // Reparent leash to animation bounds layer.
@@ -5982,9 +5986,10 @@
             return;
         }
         clearThumbnail();
+        final Transaction transaction = getAnimatingContainer().getPendingTransaction();
         mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory,
-                getPendingTransaction(), this, thumbnailHeader);
-        mThumbnail.startAnimation(getPendingTransaction(), loadThumbnailAnimation(thumbnailHeader));
+                transaction, getAnimatingContainer(), thumbnailHeader);
+        mThumbnail.startAnimation(transaction, loadThumbnailAnimation(thumbnailHeader));
     }
 
     /**
@@ -6011,13 +6016,13 @@
         if (thumbnail == null) {
             return;
         }
+        final Transaction transaction = getAnimatingContainer().getPendingTransaction();
         mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory,
-                getPendingTransaction(), this, thumbnail);
+                transaction, getAnimatingContainer(), thumbnail);
         final Animation animation =
                 getDisplayContent().mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
                         win.getFrameLw());
-        mThumbnail.startAnimation(getPendingTransaction(), animation, new Point(frame.left,
-                frame.top));
+        mThumbnail.startAnimation(transaction, animation, new Point(frame.left, frame.top));
     }
 
     private Animation loadThumbnailAnimation(GraphicBuffer thumbnailHeader) {
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index eaa0ea7..399c5d3 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -16,12 +16,8 @@
 
 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;
@@ -29,9 +25,7 @@
 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;
@@ -46,9 +40,7 @@
 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;
 
@@ -68,10 +60,10 @@
  *      animation first rotate the new content into the old orientation to then be able to
  *      animate to the new orientation
  *
- * <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 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 entering Blackframe: <p>
  *     The enter Blackframe is similar to the exit Blackframe but is only used when a custom
@@ -89,6 +81,8 @@
      */
     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;
@@ -96,18 +90,16 @@
     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;
-    /** 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 SurfaceControl mRotationLayer;
+    private SurfaceControl mSurfaceControl;
     private BlackFrame mEnteringBlackFrame;
     private int mWidth, mHeight;
 
@@ -128,11 +120,8 @@
     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) {
@@ -173,15 +162,9 @@
 
         final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
         try {
-            mBackColorSurface = displayContent.makeChildSurface(null)
-                    .setName("BackColorSurface")
-                    .setColorLayer()
-                    .build();
-
-            mScreenshotLayer = displayContent.makeOverlay()
+            mRotationLayer = displayContent.makeOverlay()
                     .setName("RotationLayer")
-                    .setBufferSize(mWidth, mHeight)
-                    .setSecure(isSecure)
+                    .setContainerLayer()
                     .build();
 
             mEnterBlackFrameLayer = displayContent.makeOverlay()
@@ -189,21 +172,26 @@
                     .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(mScreenshotLayer, Surface.SCALING_MODE_SCALE_TO_WINDOW);
+            t2.setOverrideScalingMode(mSurfaceControl, 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(mScreenshotLayer);
+            surface.copyFrom(mSurfaceControl);
             SurfaceControl.ScreenshotGraphicBuffer gb =
                     mService.mDisplayManagerInternal.screenshot(displayId);
             if (gb != null) {
-                mStartLuma = RotationAnimationUtils.getAvgBorderLuma(gb.getGraphicBuffer(),
-                        gb.getColorSpace());
                 try {
                     surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(),
                             gb.getColorSpace());
@@ -214,15 +202,13 @@
                 // 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(mScreenshotLayer, true);
+                    t.setSecure(mSurfaceControl, true);
                 }
-                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);
+                t.setLayer(mRotationLayer, SCREEN_FREEZE_LAYER_BASE);
+                t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
+                t.setAlpha(mSurfaceControl, 0);
+                t.show(mRotationLayer);
+                t.show(mSurfaceControl);
             } else {
                 Slog.w(TAG, "Unable to take screenshot of display " + displayId);
             }
@@ -232,11 +218,32 @@
         }
 
         ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
-                    "  FREEZE %s: CREATE", mScreenshotLayer);
+                    "  FREEZE %s: CREATE", mSurfaceControl);
         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);
@@ -245,11 +252,11 @@
     }
 
     boolean hasScreenshot() {
-        return mScreenshotLayer != null;
+        return mSurfaceControl != null;
     }
 
     private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) {
-        if (mScreenshotLayer == null) {
+        if (mRotationLayer == null) {
             return;
         }
         matrix.getValues(mTmpFloats);
@@ -260,19 +267,24 @@
             x -= mCurrentDisplayRect.left;
             y -= mCurrentDisplayRect.top;
         }
-        t.setPosition(mScreenshotLayer, x, y);
-        t.setMatrix(mScreenshotLayer,
+        t.setPosition(mRotationLayer, x, y);
+        t.setMatrix(mRotationLayer,
                 mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
                 mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
 
-        t.setAlpha(mScreenshotLayer, (float) 1.0);
-        t.show(mScreenshotLayer);
+        t.setAlpha(mSurfaceControl, (float) 1.0);
+        t.setAlpha(mRotationLayer, (float) 1.0);
+        t.show(mRotationLayer);
     }
 
     public void printTo(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("mSurface="); pw.print(mScreenshotLayer);
+        pw.print(prefix); pw.print("mSurface="); pw.print(mSurfaceControl);
         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);
@@ -291,10 +303,20 @@
         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.println();
+        mSnapshotInitialMatrix.printShortString(pw);
+        pw.print(" mSnapshotFinalMatrix="); mSnapshotFinalMatrix.printShortString(pw);
+        pw.println();
+        pw.print(prefix); pw.print("mExitFrameFinalMatrix=");
+        mExitFrameFinalMatrix.printShortString(pw);
+        pw.println();
         pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation);
         if (mForceDefaultOrientation) {
             pw.print(" mOriginalDisplayRect="); pw.print(mOriginalDisplayRect.toShortString());
@@ -309,7 +331,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);
-        RotationAnimationUtils.createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
+        createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
 
         setRotationTransform(t, mSnapshotInitialMatrix);
     }
@@ -319,7 +341,7 @@
      */
     private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration,
             float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
-        if (mScreenshotLayer == null) {
+        if (mSurfaceControl == null) {
             // Can't do animation.
             return false;
         }
@@ -332,58 +354,89 @@
         // 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) { /* Counter-Clockwise Rotations */
+            switch (delta) {
                 case Surface.ROTATION_0:
                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
-                            R.anim.screen_rotate_0_exit);
+                            com.android.internal.R.anim.screen_rotate_0_exit);
                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
-                            R.anim.screen_rotate_0_enter);
+                            com.android.internal.R.anim.screen_rotate_0_enter);
                     break;
                 case Surface.ROTATION_90:
                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
-                            R.anim.screen_rotate_plus_90_exit);
+                            com.android.internal.R.anim.screen_rotate_plus_90_exit);
                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
-                            R.anim.screen_rotate_plus_90_enter);
+                            com.android.internal.R.anim.screen_rotate_plus_90_enter);
                     break;
                 case Surface.ROTATION_180:
                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
-                            R.anim.screen_rotate_180_exit);
+                            com.android.internal.R.anim.screen_rotate_180_exit);
                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
-                            R.anim.screen_rotate_180_enter);
+                            com.android.internal.R.anim.screen_rotate_180_enter);
                     break;
                 case Surface.ROTATION_270:
                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
-                            R.anim.screen_rotate_minus_90_exit);
+                            com.android.internal.R.anim.screen_rotate_minus_90_exit);
                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
-                            R.anim.screen_rotate_minus_90_enter);
+                            com.android.internal.R.anim.screen_rotate_minus_90_enter);
                     break;
             }
         }
 
-        mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
-        mRotateExitAnimation.restrictDuration(maxAnimationDuration);
-        mRotateExitAnimation.scaleCurrentDuration(animationScale);
+        // 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);
-        mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
-        mRotateEnterAnimation.scaleCurrentDuration(animationScale);
-
+        mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
         mAnimRunning = false;
         mFinishAnimReady = false;
         mFinishAnimStartTime = -1;
 
-        if (customAnim) {
-            mRotateAlphaAnimation.restrictDuration(maxAnimationDuration);
-            mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
+        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 && mEnteringBlackFrame == null) {
@@ -398,12 +451,7 @@
             }
         }
 
-        if (customAnim) {
-            mSurfaceRotationAnimationController.startCustomAnimation();
-        } else {
-            mSurfaceRotationAnimationController.startScreenRotationAnimation();
-        }
-
+        mSurfaceRotationAnimationController.startAnimation();
         return true;
     }
 
@@ -412,13 +460,11 @@
      */
     public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration,
             float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
-        if (mScreenshotLayer == null) {
+        if (mSurfaceControl == null) {
             // Can't do animation.
             return false;
         }
         if (!mStarted) {
-            mEndLuma = RotationAnimationUtils.getLumaOfSurfaceControl(mDisplayContent.getDisplay(),
-                    mDisplayContent.getWindowingLayer());
             startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight,
                     exitAnim, enterAnim);
         }
@@ -434,28 +480,28 @@
             mSurfaceRotationAnimationController.cancel();
             mSurfaceRotationAnimationController = null;
         }
-
-        if (mScreenshotLayer != null) {
-            ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "  FREEZE %s: DESTROY", mScreenshotLayer);
+        if (mSurfaceControl != null) {
+            ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "  FREEZE %s: DESTROY", mSurfaceControl);
+            mSurfaceControl = null;
             SurfaceControl.Transaction t = mService.mTransactionFactory.get();
-            if (mScreenshotLayer.isValid()) {
-                t.remove(mScreenshotLayer);
+            if (mRotationLayer != null) {
+                if (mRotationLayer.isValid()) {
+                    t.remove(mRotationLayer);
+                }
+                mRotationLayer = null;
             }
-            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;
@@ -491,28 +537,18 @@
      * Utility class that runs a {@link ScreenRotationAnimation} on the {@link
      * SurfaceAnimationRunner}.
      * <p>
-     * The rotation animation supports both screen rotation and custom animations
-     *
-     * For custom animations:
+     * The rotation animation is divided into the following hierarchy:
      * <ul>
-     *   <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>
-     *
+     * <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
      * {@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
@@ -520,35 +556,22 @@
      */
     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 startScreenRotationAnimation() {
-            try {
-                mService.mSurfaceAnimationRunner.deferStartingAnimations();
-                mDisplayAnimator = startDisplayRotation();
+        void startAnimation() {
+            mRotateScreenAnimator = startScreenshotAlphaAnimation();
+            mDisplayAnimator = startDisplayRotation();
+            if (mExitingBlackFrame != null) {
                 mScreenshotRotationAnimator = startScreenshotRotationAnimation();
-                startColorAnimation();
-            } finally {
-                mService.mSurfaceAnimationRunner.continueStartingAnimations();
+            }
+            if (mEnteringBlackFrame != null) {
+                mEnterBlackFrameAnimator = startEnterBlackFrameAnimation();
             }
         }
 
@@ -573,8 +596,8 @@
 
         private SurfaceAnimator startScreenshotAlphaAnimation() {
             return startAnimation(initializeBuilder()
-                            .setSurfaceControl(mScreenshotLayer)
-                            .setAnimationLeashParent(mDisplayContent.getOverlayLayer())
+                            .setSurfaceControl(mSurfaceControl)
+                            .setAnimationLeashParent(mRotationLayer)
                             .setWidth(mWidth)
                             .setHeight(mHeight)
                             .build(),
@@ -593,67 +616,13 @@
 
         private SurfaceAnimator startScreenshotRotationAnimation() {
             return startAnimation(initializeBuilder()
-                            .setSurfaceControl(mScreenshotLayer)
+                            .setSurfaceControl(mRotationLayer)
                             .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 */);
@@ -677,6 +646,7 @@
 
             LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter(
                     animationSpec, mService.mSurfaceAnimationRunner);
+
             animator.startAnimation(mDisplayContent.getPendingTransaction(),
                     localAnimationAdapter, false);
             return animator;
@@ -722,6 +692,7 @@
             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 5633b6b..50cea2e 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -78,10 +78,6 @@
     @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/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index f3880fa..c38868a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -42,6 +42,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.logWithStack;
+import static com.android.server.wm.WindowManagerService.sHierarchicalAnimations;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 
 import android.annotation.CallSuper;
@@ -1855,7 +1856,7 @@
     // TODO: Remove this and use #getBounds() instead once we set an app transition animation
     // on TaskStack.
     Rect getAnimationBounds(int appStackClipMode) {
-        return getBounds();
+        return getDisplayedBounds();
     }
 
     /**
@@ -1929,7 +1930,11 @@
 
         // Separate position and size for use in animators.
         mTmpRect.set(getAnimationBounds(appStackClipMode));
-        mTmpPoint.set(mTmpRect.left, mTmpRect.top);
+        if (sHierarchicalAnimations) {
+            getRelativeDisplayedPosition(mTmpPoint);
+        } else {
+            mTmpPoint.set(mTmpRect.left, mTmpRect.top);
+        }
         mTmpRect.offsetTo(0, 0);
 
         final RemoteAnimationController controller =
diff --git a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
deleted file mode 100644
index 94f6676..0000000
--- a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.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_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index acb6bea..00436bb 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -204,6 +204,7 @@
 
 // Must match the value from GnssMeasurement.java
 static const uint32_t ADR_STATE_HALF_CYCLE_REPORTED = (1<<4);
+static const uint32_t SVID_FLAGS_HAS_BASEBAND_CN0 = (1<<4);
 
 sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
 sp<IGnss_V1_0> gnssHal = nullptr;
@@ -634,6 +635,16 @@
     template<class T>
     Return<void> gnssSvStatusCbImpl(const T& svStatus);
 
+    template<class T>
+    uint32_t getHasBasebandCn0DbHzFlag(const T& svStatus) {
+        return 0;
+    }
+
+    template<class T>
+    double getBasebandCn0DbHz(const T& svStatus, size_t i) {
+        return 0.0;
+    }
+
     uint32_t getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) {
         return svStatus.numSvs;
     }
@@ -658,8 +669,6 @@
 
     const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
             const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
-        // TODO(b/144850155): fill baseband CN0 after it's available in Java object.
-        ALOGD("getGnssSvInfoOfIndex %d: baseband C/N0: %f", (int) i, svInfoList[i].basebandCN0DbHz);
         return svInfoList[i].v2_0.v1_0;
     }
 
@@ -721,6 +730,18 @@
     return Void();
 }
 
+template<>
+uint32_t GnssCallback::getHasBasebandCn0DbHzFlag(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>&
+        svStatus) {
+    return SVID_FLAGS_HAS_BASEBAND_CN0;
+}
+
+template<>
+double GnssCallback::getBasebandCn0DbHz(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList,
+        size_t i) {
+    return svInfoList[i].basebandCN0DbHz;
+}
+
 template<class T>
 Return<void> GnssCallback::gnssSvStatusCbImpl(const T& svStatus) {
     JNIEnv* env = getJniEnv();
@@ -758,8 +779,8 @@
         elev[i] = info.elevationDegrees;
         azim[i] = info.azimuthDegrees;
         carrierFreq[i] = info.carrierFrequencyHz;
-        // TODO(b/144850155): fill svidWithFlags with hasBasebandCn0DbHz based on HAL versions
-        basebandCn0s[i] = 0.0;
+        svidWithFlags[i] |= getHasBasebandCn0DbHzFlag(svStatus);
+        basebandCn0s[i] = getBasebandCn0DbHz(svStatus, i);
     }
 
     env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
@@ -1185,8 +1206,8 @@
         const IGnssMeasurementCallback_V2_1::GnssMeasurement* measurement_V2_1,
         JavaObject& object) {
     translateSingleGnssMeasurement(&(measurement_V2_1->v2_0), object);
-    // TODO(b/144850155): fill baseband CN0 after it's available in Java object
-    ALOGD("baseband CN0DbHz = %f\n", measurement_V2_1->basebandCN0DbHz);
+
+    SET(BasebandCn0DbHz, measurement_V2_1->basebandCN0DbHz);
 }
 
 template<class T>
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d5ff280..6b81fdd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -129,6 +129,7 @@
 import android.app.admin.DevicePolicyManager.PasswordComplexity;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.DeviceStateCache;
+import android.app.admin.FactoryResetProtectionPolicy;
 import android.app.admin.NetworkEvent;
 import android.app.admin.PasswordMetrics;
 import android.app.admin.PasswordPolicy;
@@ -268,6 +269,7 @@
 import com.android.internal.widget.PasswordValidationError;
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
+import com.android.server.PersistentDataBlockManagerInternal;
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.SystemService;
 import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
@@ -1006,6 +1008,8 @@
         private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL =
                 "cross-profile-calendar-packages-null";
         private static final String TAG_CROSS_PROFILE_PACKAGES = "cross-profile-packages";
+        private static final String TAG_FACTORY_RESET_PROTECTION_POLICY =
+                "factory_reset_protection_policy";
 
         DeviceAdminInfo info;
 
@@ -1016,6 +1020,9 @@
         @NonNull
         PasswordPolicy mPasswordPolicy = new PasswordPolicy();
 
+        @Nullable
+        FactoryResetProtectionPolicy mFactoryResetProtectionPolicy = null;
+
         static final long DEF_MAXIMUM_TIME_TO_UNLOCK = 0;
         long maximumTimeToUnlock = DEF_MAXIMUM_TIME_TO_UNLOCK;
 
@@ -1351,6 +1358,11 @@
                         mCrossProfileCalendarPackages);
             }
             writePackageListToXml(out, TAG_CROSS_PROFILE_PACKAGES, mCrossProfilePackages);
+            if (mFactoryResetProtectionPolicy != null) {
+                out.startTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
+                mFactoryResetProtectionPolicy.writeToXml(out);
+                out.endTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
+            }
         }
 
         void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException {
@@ -1584,6 +1596,9 @@
                     mCrossProfileCalendarPackages = null;
                 } else if (TAG_CROSS_PROFILE_PACKAGES.equals(tag)) {
                     mCrossProfilePackages = readPackageList(parser, tag);
+                } else if (TAG_FACTORY_RESET_PROTECTION_POLICY.equals(tag)) {
+                    mFactoryResetProtectionPolicy = FactoryResetProtectionPolicy.readFromXml(
+                                parser);
                 } else {
                     Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -2036,6 +2051,10 @@
             return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE));
         }
 
+        PersistentDataBlockManagerInternal getPersistentDataBlockManagerInternal() {
+            return LocalServices.getService(PersistentDataBlockManagerInternal.class);
+        }
+
         LockSettingsInternal getLockSettingsInternal() {
             return LocalServices.getService(LockSettingsInternal.class);
         }
@@ -6736,6 +6755,67 @@
     }
 
     @Override
+    public void setFactoryResetProtectionPolicy(ComponentName who,
+            @Nullable FactoryResetProtectionPolicy policy) {
+        if (!mHasFeature) {
+            return;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+
+        final int frpManagementAgentUid = getFrpManagementAgentUidOrThrow();
+        final int userId = mInjector.userHandleGetCallingUserId();
+        synchronized (getLockObject()) {
+            ActiveAdmin admin = getActiveAdminForCallerLocked(
+                    who, DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
+            admin.mFactoryResetProtectionPolicy = policy;
+            saveSettingsLocked(userId);
+        }
+
+        mInjector.binderWithCleanCallingIdentity(() -> mContext.sendBroadcastAsUser(
+                new Intent(DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
+                UserHandle.getUserHandleForUid(frpManagementAgentUid)));
+
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_FACTORY_RESET_PROTECTION)
+                .setAdmin(who)
+                .write();
+    }
+
+    @Override
+    public FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(
+            @Nullable ComponentName who) {
+        if (!mHasFeature) {
+            return null;
+        }
+
+        final int frpManagementAgentUid = getFrpManagementAgentUidOrThrow();
+        ActiveAdmin admin;
+        synchronized (getLockObject()) {
+            if (who == null) {
+                if ((frpManagementAgentUid != mInjector.binderGetCallingUid())) {
+                    throw new SecurityException(
+                            "Must be called by the FRP management agent on device");
+                }
+                admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+                        UserHandle.getUserId(frpManagementAgentUid));
+            } else {
+                admin = getActiveAdminForCallerLocked(
+                        who, DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
+            }
+        }
+        return admin != null ? admin.mFactoryResetProtectionPolicy : null;
+    }
+
+    private int getFrpManagementAgentUidOrThrow() {
+        PersistentDataBlockManagerInternal pdb = mInjector.getPersistentDataBlockManagerInternal();
+        if ((pdb == null) || (pdb.getAllowedUid() == -1)) {
+            throw new UnsupportedOperationException(
+                    "The persistent data block service is not supported on this device");
+        }
+        return pdb.getAllowedUid();
+    }
+
+    @Override
     public void getRemoveWarning(ComponentName comp, final RemoteCallback result, int userHandle) {
         if (!mHasFeature) {
             return;
@@ -8131,6 +8211,14 @@
         return null;
     }
 
+    ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(int userId) {
+        ActiveAdmin admin = getDeviceOwnerAdminLocked();
+        if (admin == null) {
+            admin = getProfileOwnerOfOrganizationOwnedDeviceLocked(userId);
+        }
+        return admin;
+    }
+
     @Override
     public void clearDeviceOwner(String packageName) {
         Objects.requireNonNull(packageName, "packageName is null");
@@ -12212,6 +12300,7 @@
                 case DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE:
                     return checkManagedProfileProvisioningPreCondition(packageName, callingUserId);
                 case DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE:
+                case DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE:
                     return checkDeviceOwnerProvisioningPreCondition(callingUserId);
                 case DevicePolicyManager.ACTION_PROVISION_MANAGED_USER:
                     return checkManagedUserProvisioningPreCondition(callingUserId);
diff --git a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java b/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
index a8a258f..9c5d4ad 100644
--- a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
+++ b/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
@@ -3,6 +3,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 
 import android.os.Looper;
 import android.os.SystemClock;
@@ -52,8 +53,10 @@
 
     @Test
     public void handleInjectNtpTime_cachedAgeLow_injectTime() throws InterruptedException {
-        doReturn(NtpTimeHelper.NTP_INTERVAL - 1).when(mMockNtpTrustedTime).getCacheAge();
-        doReturn(MOCK_NTP_TIME).when(mMockNtpTrustedTime).getCachedNtpTime();
+        NtpTrustedTime.TimeResult result = mock(NtpTrustedTime.TimeResult.class);
+        doReturn(NtpTimeHelper.NTP_INTERVAL - 1).when(result).getAgeMillis();
+        doReturn(MOCK_NTP_TIME).when(result).getTimeMillis();
+        doReturn(result).when(mMockNtpTrustedTime).getCachedTimeResult();
 
         mNtpTimeHelper.retrieveAndInjectNtpTime();
 
@@ -64,7 +67,9 @@
     @Test
     public void handleInjectNtpTime_injectTimeFailed_injectTimeDelayed()
             throws InterruptedException {
-        doReturn(NtpTimeHelper.NTP_INTERVAL + 1).when(mMockNtpTrustedTime).getCacheAge();
+        NtpTrustedTime.TimeResult result1 = mock(NtpTrustedTime.TimeResult.class);
+        doReturn(NtpTimeHelper.NTP_INTERVAL + 1).when(result1).getAgeMillis();
+        doReturn(result1).when(mMockNtpTrustedTime).getCachedTimeResult();
         doReturn(false).when(mMockNtpTrustedTime).forceRefresh();
 
         mNtpTimeHelper.retrieveAndInjectNtpTime();
@@ -72,8 +77,10 @@
         assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isFalse();
 
         doReturn(true).when(mMockNtpTrustedTime).forceRefresh();
-        doReturn(1L).when(mMockNtpTrustedTime).getCacheAge();
-        doReturn(MOCK_NTP_TIME).when(mMockNtpTrustedTime).getCachedNtpTime();
+        NtpTrustedTime.TimeResult result2 = mock(NtpTrustedTime.TimeResult.class);
+        doReturn(1L).when(result2).getAgeMillis();
+        doReturn(MOCK_NTP_TIME).when(result2).getTimeMillis();
+        doReturn(result2).when(mMockNtpTrustedTime).getCachedTimeResult();
         SystemClock.sleep(NtpTimeHelper.RETRY_INTERVAL);
 
         waitForTasksToBePostedOnHandlerAndRunThem();
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 556f96a..6a5de84 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -345,8 +345,8 @@
     }
 
     /**
-     * Lowers quotas to make testing feasible.
-     * Careful while calling as this will replace any existing settings for the calling test.
+     * Lowers quotas to make testing feasible. Careful while calling as this will replace any
+     * existing settings for the calling test.
      */
     private void setTestableQuotas() {
         final StringBuilder constantsBuilder = new StringBuilder();
@@ -981,6 +981,25 @@
         assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
     }
 
+    @Test
+    public void alarmCountOnListenerBinderDied() {
+        final int numAlarms = 10;
+        final IAlarmListener[] listeners = new IAlarmListener[numAlarms];
+        for (int i = 0; i < numAlarms; i++) {
+            listeners[i] = new IAlarmListener.Stub() {
+                @Override
+                public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
+                }
+            };
+            setTestAlarmWithListener(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + i, listeners[i]);
+        }
+        assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+        for (int i = 0; i < numAlarms; i++) {
+            mService.mListenerDeathRecipient.binderDied(listeners[i].asBinder());
+            assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+        }
+    }
+
     @After
     public void tearDown() {
         if (mMockingSession != null) {
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 591c3a3..c223f13 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -804,6 +804,11 @@
         }
 
         @Override
+        public boolean switchToInputMethod(String imeId) {
+            return false;
+        }
+
+        @Override
         public boolean isAccessibilityButtonAvailable() throws RemoteException {
             return false;
         }
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
index d44476e..3de006c 100644
--- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -57,7 +57,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InOrder;
@@ -523,7 +522,6 @@
      * Test that {@link BackupManagerService#dump()} dumps system user information before non-system
      * user information.
      */
-
     @Test
     public void testDump_systemUserFirst() {
         String[] args = new String[0];
@@ -539,16 +537,31 @@
     }
 
     @Test
-    @Ignore("b/147012496")
-    public void testGetUserForAncestralSerialNumber() {
+    public void testGetUserForAncestralSerialNumber_forSystemUser() {
         BackupManagerServiceTestable.sBackupDisabled = false;
         BackupManagerService backupManagerService =
                 new BackupManagerServiceTestable(mContextMock, mUserServices);
+        when(mUserManagerMock.getProfileIds(UserHandle.getCallingUserId(), false))
+                .thenReturn(new int[] {UserHandle.USER_SYSTEM, NON_USER_SYSTEM});
         when(mUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
 
         UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L);
 
-        assertThat(user).isEqualTo(UserHandle.of(1));
+        assertThat(user).isEqualTo(UserHandle.of(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void testGetUserForAncestralSerialNumber_forNonSystemUser() {
+        BackupManagerServiceTestable.sBackupDisabled = false;
+        BackupManagerService backupManagerService =
+                new BackupManagerServiceTestable(mContextMock, mUserServices);
+        when(mUserManagerMock.getProfileIds(UserHandle.getCallingUserId(), false))
+                .thenReturn(new int[] {UserHandle.USER_SYSTEM, NON_USER_SYSTEM});
+        when(mNonSystemUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
+
+        UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L);
+
+        assertThat(user).isEqualTo(UserHandle.of(NON_USER_SYSTEM));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index ac555fd..3a8258b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -51,6 +51,7 @@
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
+import com.android.server.PersistentDataBlockManagerInternal;
 import com.android.server.net.NetworkPolicyManagerInternal;
 
 import java.io.File;
@@ -223,6 +224,11 @@
         }
 
         @Override
+        PersistentDataBlockManagerInternal getPersistentDataBlockManagerInternal() {
+            return services.persistentDataBlockManagerInternal;
+        }
+
+        @Override
         Looper getMyLooper() {
             return Looper.getMainLooper();
         }
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 175c756..21034d3 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -31,6 +31,8 @@
 import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
 import static com.android.server.testutils.TestUtils.assertExpectException;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyLong;
@@ -62,6 +64,7 @@
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.FactoryResetProtectionPolicy;
 import android.app.admin.PasswordMetrics;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
@@ -2022,6 +2025,116 @@
         );
     }
 
+    public void testSetFactoryResetProtectionPolicyWithDO() throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+
+        when(getServices().persistentDataBlockManagerInternal.getAllowedUid()).thenReturn(
+                DpmMockContext.CALLER_UID);
+
+        FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
+                .setFactoryResetProtectionAccounts(new ArrayList<>())
+                .setFactoryResetProtectionDisabled(true)
+                .build();
+        dpm.setFactoryResetProtectionPolicy(admin1, policy);
+
+        FactoryResetProtectionPolicy result = dpm.getFactoryResetProtectionPolicy(admin1);
+        assertThat(result).isEqualTo(policy);
+        assertPoliciesAreEqual(policy, result);
+
+        verify(mContext.spiedContext).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(
+                        DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+    }
+
+    public void testSetFactoryResetProtectionPolicyFailWithPO() throws Exception {
+        setupProfileOwner();
+
+        FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
+                .setFactoryResetProtectionDisabled(true)
+                .build();
+
+        assertExpectException(SecurityException.class, null,
+                () -> dpm.setFactoryResetProtectionPolicy(admin1, policy));
+    }
+
+    public void testSetFactoryResetProtectionPolicyWithPOOfOrganizationOwnedDevice()
+            throws Exception {
+        setupProfileOwner();
+        configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+        when(getServices().persistentDataBlockManagerInternal.getAllowedUid()).thenReturn(
+                DpmMockContext.CALLER_UID);
+
+        List<String> accounts = new ArrayList<>();
+        accounts.add("Account 1");
+        accounts.add("Account 2");
+
+        FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
+                .setFactoryResetProtectionAccounts(accounts)
+                .build();
+
+        dpm.setFactoryResetProtectionPolicy(admin1, policy);
+
+        FactoryResetProtectionPolicy result = dpm.getFactoryResetProtectionPolicy(admin1);
+        assertThat(result).isEqualTo(policy);
+        assertPoliciesAreEqual(policy, result);
+
+        verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(
+                        DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+        verify(mContext.spiedContext).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(
+                        DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED),
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+        verify(mContext.spiedContext).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(
+                        DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+    }
+
+    public void testGetFactoryResetProtectionPolicyWithFrpManagementAgent()
+            throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+        when(getServices().persistentDataBlockManagerInternal.getAllowedUid()).thenReturn(
+                DpmMockContext.CALLER_UID);
+
+        FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
+                .setFactoryResetProtectionAccounts(new ArrayList<>())
+                .setFactoryResetProtectionDisabled(true)
+                .build();
+
+        dpm.setFactoryResetProtectionPolicy(admin1, policy);
+
+        mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        dpm.setActiveAdmin(admin1, /*replace=*/ false);
+        FactoryResetProtectionPolicy result = dpm.getFactoryResetProtectionPolicy(null);
+        assertThat(result).isEqualTo(policy);
+        assertPoliciesAreEqual(policy, result);
+
+        verify(mContext.spiedContext).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(
+                        DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+    }
+
+    private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy,
+            FactoryResetProtectionPolicy actualPolicy) {
+        assertThat(actualPolicy.isFactoryResetProtectionDisabled()).isEqualTo(
+                expectedPolicy.isFactoryResetProtectionDisabled());
+        assertAccountsAreEqual(expectedPolicy.getFactoryResetProtectionAccounts(),
+                actualPolicy.getFactoryResetProtectionAccounts());
+    }
+
+    private void assertAccountsAreEqual(List<String> expectedAccounts,
+            List<String> actualAccounts) {
+        assertThat(actualAccounts).containsExactlyElementsIn(expectedAccounts);
+    }
+
     public void testGetMacAddress() throws Exception {
         mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
@@ -2845,6 +2958,7 @@
         mContext.packageName = admin1.getPackageName();
         setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, false);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE, false);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
                 false);
@@ -2856,6 +2970,8 @@
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED);
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
+                DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED);
         assertCheckProvisioningPreCondition(
@@ -2882,6 +2998,7 @@
         mContext.packageName = admin1.getPackageName();
         setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
                 false);
@@ -2890,6 +3007,7 @@
         // Test again when split user is on
         when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
                 true);
@@ -2901,6 +3019,8 @@
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_OK);
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
+                DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_MANAGED_USERS_NOT_SUPPORTED);
         assertCheckProvisioningPreCondition(
@@ -2913,6 +3033,8 @@
         when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_OK);
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
+                DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_MANAGED_USERS_NOT_SUPPORTED);
         assertCheckProvisioningPreCondition(
@@ -2938,6 +3060,7 @@
         mContext.packageName = admin1.getPackageName();
         setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
                 false /* because of non-split user */);
@@ -2951,6 +3074,8 @@
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_OK);
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
+                DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(
@@ -2995,6 +3120,8 @@
         setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 false/* because of completed device setup */);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
+                false/* because of completed device setup */);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
                 false/* because of non-split user */);
@@ -3008,6 +3135,8 @@
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_USER_SETUP_COMPLETED);
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
+                DevicePolicyManager.CODE_USER_SETUP_COMPLETED);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(
@@ -3024,7 +3153,10 @@
 
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_HAS_DEVICE_OWNER);
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
+                DevicePolicyManager.CODE_HAS_DEVICE_OWNER);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, false);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE, false);
 
         // COMP mode is allowed.
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
@@ -3153,6 +3285,7 @@
         mContext.packageName = admin1.getPackageName();
         setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 false /* because canAddMoreManagedProfiles returns false */);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
@@ -3167,6 +3300,8 @@
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_OK);
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
+                DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER);
         assertCheckProvisioningPreCondition(
@@ -3193,6 +3328,8 @@
         setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 true/* it's undefined behavior. Can be changed into false in the future */);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
+                true/* it's undefined behavior. Can be changed into false in the future */);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 false /* because canAddMoreManagedProfiles returns false */);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
@@ -3207,6 +3344,8 @@
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_OK);
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
+                DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER);
         assertCheckProvisioningPreCondition(
@@ -3232,6 +3371,7 @@
         mContext.packageName = admin1.getPackageName();
         setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
                 true);
@@ -3244,6 +3384,8 @@
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_OK);
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
+                DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(
@@ -3271,6 +3413,8 @@
         setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 true/* it's undefined behavior. Can be changed into false in the future */);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
+                true/* it's undefined behavior. Can be changed into false in the future */);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
                 true/* it's undefined behavior. Can be changed into false in the future */);
@@ -3284,6 +3428,8 @@
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_OK);
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
+                DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
new file mode 100644
index 0000000..bc853c6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.devicepolicy;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.FactoryResetProtectionPolicy;
+import android.os.Parcel;
+import android.util.Xml;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link android.app.admin.FactoryResetProtectionPolicy}.
+ *
+ * atest com.android.server.devicepolicy.FactoryResetProtectionPolicyTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class FactoryResetProtectionPolicyTest {
+
+    private static final String TAG_FACTORY_RESET_PROTECTION_POLICY =
+            "factory_reset_protection_policy";
+
+    @Test
+    public void testNonDefaultFactoryResetProtectionPolicyObject() throws Exception {
+        List<String> accounts = new ArrayList<>();
+        accounts.add("Account 1");
+        accounts.add("Account 2");
+
+        FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
+                .setFactoryResetProtectionAccounts(accounts)
+                .setFactoryResetProtectionDisabled(true)
+                .build();
+
+        testParcelAndUnparcel(policy);
+        testSerializationAndDeserialization(policy);
+    }
+
+    @Test
+    public void testInvalidXmlFactoryResetProtectionPolicyObject() throws Exception {
+        List<String> accounts = new ArrayList<>();
+        accounts.add("Account 1");
+        accounts.add("Account 2");
+
+        FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
+                .setFactoryResetProtectionAccounts(accounts)
+                .setFactoryResetProtectionDisabled(true)
+                .build();
+
+        testParcelAndUnparcel(policy);
+        testInvalidXmlSerializationAndDeserialization(policy);
+    }
+
+    private void testParcelAndUnparcel(FactoryResetProtectionPolicy policy) {
+        Parcel parcel = Parcel.obtain();
+        policy.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        FactoryResetProtectionPolicy actualPolicy =
+                FactoryResetProtectionPolicy.CREATOR.createFromParcel(parcel);
+        assertPoliciesAreEqual(policy, actualPolicy);
+        parcel.recycle();
+    }
+
+    private void testSerializationAndDeserialization(FactoryResetProtectionPolicy policy)
+            throws Exception {
+        ByteArrayOutputStream outStream = serialize(policy);
+        ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new InputStreamReader(inStream));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+
+        assertPoliciesAreEqual(policy, policy.readFromXml(parser));
+    }
+
+    private void testInvalidXmlSerializationAndDeserialization(FactoryResetProtectionPolicy policy)
+            throws Exception {
+        ByteArrayOutputStream outStream = serialize(policy);
+        ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
+        XmlPullParser parser = mock(XmlPullParser.class);
+        when(parser.next()).thenThrow(XmlPullParserException.class);
+        parser.setInput(new InputStreamReader(inStream));
+
+        // If deserialization fails, then null is returned.
+        assertNull(policy.readFromXml(parser));
+    }
+
+    private ByteArrayOutputStream serialize(FactoryResetProtectionPolicy policy)
+            throws IOException {
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+        final XmlSerializer outXml = new FastXmlSerializer();
+        outXml.setOutput(outStream, StandardCharsets.UTF_8.name());
+        outXml.startDocument(null, true);
+        outXml.startTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
+        policy.writeToXml(outXml);
+        outXml.endTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
+        outXml.endDocument();
+        outXml.flush();
+        return outStream;
+    }
+
+    private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy,
+            FactoryResetProtectionPolicy actualPolicy) {
+        assertEquals(expectedPolicy.isFactoryResetProtectionDisabled(),
+                actualPolicy.isFactoryResetProtectionDisabled());
+        assertAccountsAreEqual(expectedPolicy.getFactoryResetProtectionAccounts(),
+                actualPolicy.getFactoryResetProtectionAccounts());
+    }
+
+    private void assertAccountsAreEqual(List<String> expectedAccounts,
+            List<String> actualAccounts) {
+        assertEquals(expectedAccounts.size(), actualAccounts.size());
+        for (int i = 0; i < expectedAccounts.size(); i++) {
+            assertEquals(expectedAccounts.get(i), actualAccounts.get(i));
+        }
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 919a3f6..6c2c144 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -68,6 +68,7 @@
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
+import com.android.server.PersistentDataBlockManagerInternal;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -117,6 +118,7 @@
     public final TimeDetector timeDetector;
     public final TimeZoneDetector timeZoneDetector;
     public final KeyChain.KeyChainConnection keyChainConnection;
+    public final PersistentDataBlockManagerInternal persistentDataBlockManagerInternal;
     /** Note this is a partial mock, not a real mock. */
     public final PackageManager packageManager;
     public final BuildMock buildMock = new BuildMock();
@@ -160,6 +162,7 @@
         timeDetector = mock(TimeDetector.class);
         timeZoneDetector = mock(TimeZoneDetector.class);
         keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS);
+        persistentDataBlockManagerInternal = mock(PersistentDataBlockManagerInternal.class);
 
         // Package manager is huge, so we use a partial mock instead.
         packageManager = spy(realContext.getPackageManager());
diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
index 63189e7..5aed194 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
@@ -16,14 +16,7 @@
 
 package com.android.server.integrity;
 
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.hasItems;
-import static org.junit.Assert.assertThat;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.content.integrity.AppInstallMetadata;
 import android.content.integrity.AtomicFormula;
@@ -33,26 +26,26 @@
 import android.content.integrity.Rule;
 import android.util.Slog;
 
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.integrity.parser.RuleXmlParser;
-import com.android.server.integrity.serializer.RuleXmlSerializer;
+import com.android.server.integrity.parser.RuleBinaryParser;
+import com.android.server.integrity.serializer.RuleBinarySerializer;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 import java.io.File;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
 /** Unit test for {@link IntegrityFileManager} */
-@RunWith(AndroidJUnit4.class)
+@RunWith(JUnit4.class)
 public class IntegrityFileManagerTest {
     private static final String TAG = "IntegrityFileManagerTest";
 
@@ -72,7 +65,7 @@
         // Use Xml Parser/Serializer to help with debugging since we can just print the file.
         mIntegrityFileManager =
                 new IntegrityFileManager(
-                        new RuleXmlParser(), new RuleXmlSerializer(), mTmpDir);
+                        new RuleBinaryParser(), new RuleBinarySerializer(), mTmpDir);
         Files.walk(mTmpDir.toPath())
                 .forEach(
                         path -> {
@@ -97,12 +90,19 @@
 
     @Test
     public void testGetMetadata() throws Exception {
-        assertNull(mIntegrityFileManager.readMetadata());
+        assertThat(mIntegrityFileManager.readMetadata()).isNull();
         mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
 
-        assertNotNull(mIntegrityFileManager.readMetadata());
-        assertEquals(VERSION, mIntegrityFileManager.readMetadata().getVersion());
-        assertEquals(RULE_PROVIDER, mIntegrityFileManager.readMetadata().getRuleProvider());
+        assertThat(mIntegrityFileManager.readMetadata()).isNotNull();
+        assertThat(mIntegrityFileManager.readMetadata().getVersion()).isEqualTo(VERSION);
+        assertThat(mIntegrityFileManager.readMetadata().getRuleProvider()).isEqualTo(RULE_PROVIDER);
+    }
+
+    @Test
+    public void testIsInitialized() throws Exception {
+        assertThat(mIntegrityFileManager.initialized()).isFalse();
+        mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
+        assertThat(mIntegrityFileManager.initialized()).isTrue();
     }
 
     @Test
@@ -110,20 +110,8 @@
         String packageName = "package";
         String packageCert = "cert";
         int version = 123;
-        Rule packageNameRule =
-                new Rule(
-                        new StringAtomicFormula(
-                                AtomicFormula.PACKAGE_NAME,
-                                packageName,
-                                /* isHashedValue= */ false),
-                        Rule.DENY);
-        Rule packageCertRule =
-                new Rule(
-                        new StringAtomicFormula(
-                                AtomicFormula.APP_CERTIFICATE,
-                                packageCert,
-                                /* isHashedValue= */ false),
-                        Rule.DENY);
+        Rule packageNameRule = getPackageNameIndexedRule(packageName);
+        Rule packageCertRule = getAppCertificateIndexedRule(packageCert);
         Rule versionCodeRule =
                 new Rule(
                         new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, version),
@@ -142,9 +130,7 @@
                                                 AtomicFormula.LE,
                                                 version))),
                         Rule.DENY);
-        // We will test the specifics of indexing in other classes. Here, we just require that all
-        // rules that are related to the given AppInstallMetadata are returned and do not assert
-        // anything on other rules.
+
         List<Rule> rules =
                 Arrays.asList(packageNameRule, packageCertRule, versionCodeRule, randomRule);
         mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules);
@@ -159,17 +145,77 @@
                 .build();
         List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata);
 
-        assertThat(rulesFetched, hasItems(
-                equalTo(packageNameRule),
-                equalTo(packageCertRule),
-                equalTo(versionCodeRule)
-        ));
+        assertThat(rulesFetched)
+                .containsExactly(packageNameRule, packageCertRule, versionCodeRule, randomRule);
     }
 
     @Test
-    public void testIsInitialized() throws Exception {
-        assertFalse(mIntegrityFileManager.initialized());
-        mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
-        assertTrue(mIntegrityFileManager.initialized());
+    public void testGetRules_indexedForManyRules() throws Exception {
+        String packageName = "package";
+        String installerName = "installer";
+        String appCertificate = "cert";
+
+        // Create a rule set with 2500 package name indexed, 2500 app certificate indexed and
+        // 500 unindexed rules.
+        List<Rule> rules = new ArrayList<>();
+
+        for (int i = 0; i < 2500; i++) {
+            rules.add(getPackageNameIndexedRule(String.format("%s%04d", packageName, i)));
+            rules.add(getAppCertificateIndexedRule(String.format("%s%04d", appCertificate, i)));
+        }
+
+        for (int i = 0; i < 70; i++) {
+            rules.add(getInstallerCertificateRule(String.format("%s%04d", installerName, i)));
+        }
+
+        // Write the rules and get them indexed.
+        mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules);
+
+        // Read the rules for a specific rule.
+        String installedPackageName = String.format("%s%04d", packageName, 264);
+        String installedAppCertificate = String.format("%s%04d", appCertificate, 1264);
+        AppInstallMetadata appInstallMetadata = new AppInstallMetadata.Builder()
+                .setPackageName(installedPackageName)
+                .setAppCertificate(installedAppCertificate)
+                .setVersionCode(250)
+                .setInstallerName("abc")
+                .setInstallerCertificate("abc")
+                .setIsPreInstalled(true)
+                .build();
+        List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata);
+
+        // Verify that we do not load all the rules and we have the necessary rules to evaluate.
+        assertThat(rulesFetched.size()).isEqualTo(270);
+        assertThat(rulesFetched)
+                .containsAllOf(
+                        getPackageNameIndexedRule(installedPackageName),
+                        getAppCertificateIndexedRule(installedAppCertificate));
+    }
+
+    private Rule getPackageNameIndexedRule(String packageName) {
+        return new Rule(
+                new StringAtomicFormula(
+                        AtomicFormula.PACKAGE_NAME,
+                        packageName,
+                        /* isHashedValue= */ false),
+                Rule.DENY);
+    }
+
+    private Rule getAppCertificateIndexedRule(String appCertificate) {
+        return new Rule(
+                new StringAtomicFormula(
+                        AtomicFormula.APP_CERTIFICATE,
+                        appCertificate,
+                        /* isHashedValue= */ false),
+                Rule.DENY);
+    }
+
+    private Rule getInstallerCertificateRule(String installerCert) {
+        return new Rule(
+                new StringAtomicFormula(
+                        AtomicFormula.INSTALLER_NAME,
+                        installerCert,
+                        /* isHashedValue= */ false),
+                Rule.DENY);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java
index 1eb5eb5..4fa7302 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java
@@ -121,6 +121,26 @@
     }
 
     @Test
+    public void testBitTrackedInputStream_canReadMoreRules() throws IOException {
+        String packageName = "com.test.app";
+        byte[] testInput =
+                getBytes(
+                        IS_NOT_HASHED
+                                + getBits(packageName.length(), VALUE_SIZE_BITS)
+                                + getValueBits(packageName));
+
+        BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(testInput);
+        assertThat(bitTrackedInputStream.canReadMoreRules(2)).isTrue();
+
+        // Read until the string parameter.
+        String stringValue = BinaryFileOperations.getStringValue(bitTrackedInputStream);
+
+        // Verify that the read bytes are counted.
+        assertThat(stringValue).isEqualTo(packageName);
+        assertThat(bitTrackedInputStream.canReadMoreRules(2)).isFalse();
+    }
+
+    @Test
     public void testBitTrackedInputStream_moveCursorForwardFailsIfAlreadyRead() throws IOException {
         String packageName = "com.test.app";
         byte[] testInput =
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
index 51f5c75..881b3d6 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
@@ -48,7 +48,6 @@
 import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -656,65 +655,4 @@
                 /* expectedExceptionMessageRegex */ "A rule must end with a '1' bit",
                 () -> binaryParser.parse(rule.array()));
     }
-
-    @Test
-    public void testBinaryStream_multipleRules_indexingIdentifiesParsesIndexRangeCorrectly()
-            throws Exception {
-        String packageName2 = "com.test.2";
-
-        byte[] ruleBytes1 = getBytes(getRulesWithPackageName("com.test.1"));
-        byte[] ruleBytes2 = getBytes(getRulesWithPackageName(packageName2));
-        byte[] ruleBytes3 = getBytes(getRulesWithPackageName("com.test.3"));
-
-        ByteBuffer rule =
-                ByteBuffer.allocate(
-                        DEFAULT_FORMAT_VERSION_BYTES.length
-                                + ruleBytes1.length
-                                + ruleBytes2.length
-                                + ruleBytes3.length);
-        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
-        rule.put(ruleBytes1);
-        rule.put(ruleBytes2);
-        rule.put(ruleBytes3);
-        InputStream inputStream = new ByteArrayInputStream(rule.array());
-
-        RuleParser binaryParser = new RuleBinaryParser();
-
-        List<RuleIndexRange> indexRanges = new ArrayList<>();
-        indexRanges.add(
-                new RuleIndexRange(
-                        DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length,
-                        DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length
-                                + ruleBytes2.length));
-        List<Rule> rules = binaryParser.parse(inputStream, indexRanges);
-
-        Rule expectedRule =
-                new Rule(
-                        new CompoundFormula(
-                                CompoundFormula.NOT,
-                                Collections.singletonList(
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.PACKAGE_NAME,
-                                                packageName2,
-                                                /* isHashedValue= */ false))),
-                        Rule.DENY);
-
-        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
-    }
-
-    private static String getRulesWithPackageName(String packageName) {
-        return START_BIT
-                + COMPOUND_FORMULA_START_BITS
-                + NOT
-                + ATOMIC_FORMULA_START_BITS
-                + PACKAGE_NAME
-                + EQ
-                + IS_NOT_HASHED
-                + getBits(packageName.length(), VALUE_SIZE_BITS)
-                + getValueBits(packageName)
-                + COMPOUND_FORMULA_END_BITS
-                + DENY
-                + END_BIT;
-
-    }
 }
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
index 742952e..291cfee 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
@@ -115,7 +115,8 @@
                                 + getKeyValueString(START_INDEXING_KEY, 500)
                                 + getKeyValueString(END_INDEXING_KEY, 900)
                                 + getKeyValueString(START_INDEXING_KEY, 900)
-                                + getKeyValueString(END_INDEXING_KEY, 945));
+                                + getKeyValueString(END_INDEXING_KEY, 945)
+                                + getBits(1, 1));
         ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
         rule.put(stringBytes);
         InputStream inputStream = new ByteArrayInputStream(rule.array());
@@ -152,7 +153,8 @@
                                 + getKeyValueString("888", 800)
                                 + getKeyValueString(END_INDEXING_KEY, 900)
                                 + getKeyValueString(START_INDEXING_KEY, 900)
-                                + getKeyValueString(END_INDEXING_KEY, 945));
+                                + getKeyValueString(END_INDEXING_KEY, 945)
+                                + getBits(1, 1));
         ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
         rule.put(stringBytes);
         return new ByteArrayInputStream(rule.array());
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
index eb6698b..bc2aac0 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
@@ -135,15 +135,14 @@
                 .isEqualTo(expectedRuleOutputStream.toByteArray());
 
         ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream();
+        String serializedIndexingBytes =
+                SERIALIZED_START_INDEXING_KEY
+                        + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+                        + SERIALIZED_END_INDEXING_KEY
+                        + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */32);
         byte[] expectedIndexingBytes =
-                getBytes(
-                        SERIALIZED_START_INDEXING_KEY
-                                + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
-                                + SERIALIZED_END_INDEXING_KEY
-                                + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */
-                                32));
-        expectedIndexingOutputStream.write(expectedIndexingBytes);
-        expectedIndexingOutputStream.write(expectedIndexingBytes);
+                getBytes(serializedIndexingBytes + serializedIndexingBytes
+                        + serializedIndexingBytes + getBits(1, 1));
         expectedIndexingOutputStream.write(expectedIndexingBytes);
         assertThat(indexingOutputStream.toByteArray())
                 .isEqualTo(expectedIndexingOutputStream.toByteArray());
@@ -197,15 +196,16 @@
                         + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
                         + SERIALIZED_END_INDEXING_KEY
                         + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32);
-        expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForIndexed));
-        expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForIndexed));
         String expectedIndexingBitsForUnindexed =
                 SERIALIZED_START_INDEXING_KEY
                         + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
                         + SERIALIZED_END_INDEXING_KEY
-                        + getBits(DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(
-                        expectedBits).length, /* numOfBits= */ 32);
-        expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForUnindexed));
+                        + getBits(DEFAULT_FORMAT_VERSION_BYTES.length
+                        + getBytes(expectedBits).length, /* numOfBits= */ 32);
+        expectedIndexingOutputStream.write(getBytes(
+                expectedIndexingBitsForIndexed + expectedIndexingBitsForIndexed
+                        + expectedIndexingBitsForUnindexed + getBits(1, 1)));
+
         assertThat(indexingOutputStream.toByteArray())
                 .isEqualTo(expectedIndexingOutputStream.toByteArray());
     }
@@ -564,7 +564,6 @@
         expectedIndexingBytesForPackageNameIndexed +=
                 SERIALIZED_END_INDEXING_KEY
                         + getBits(totalBytesWritten, /* numOfBits= */ 32);
-        expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForPackageNameIndexed));
 
         String expectedIndexingBytesForAppCertificateIndexed =
                 SERIALIZED_START_INDEXING_KEY
@@ -588,7 +587,6 @@
         expectedIndexingBytesForAppCertificateIndexed +=
                 SERIALIZED_END_INDEXING_KEY
                         + getBits(totalBytesWritten, /* numOfBits= */ 32);
-        expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForAppCertificateIndexed));
 
         String expectedIndexingBytesForUnindexed =
                 SERIALIZED_START_INDEXING_KEY
@@ -603,8 +601,11 @@
         expectedIndexingBytesForUnindexed +=
                 SERIALIZED_END_INDEXING_KEY
                         + getBits(totalBytesWritten, /* numOfBits= */ 32);
-        expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForUnindexed));
-
+        expectedIndexingOutputStream.write(
+                getBytes(expectedIndexingBytesForPackageNameIndexed
+                        + expectedIndexingBytesForAppCertificateIndexed
+                        + expectedIndexingBytesForUnindexed
+                        + getBits(1, 1)));
 
         assertThat(ruleOutputStream.toByteArray())
                 .isEqualTo(expectedOrderedRuleOutputStream.toByteArray());
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
index 55fada4..1674422 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
@@ -38,10 +38,8 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.TreeMap;
 
 /** Unit tests for {@link RuleIndexingDetailsIdentifier}. */
 @RunWith(JUnit4.class)
@@ -140,7 +138,7 @@
         List<Rule> ruleList = new ArrayList();
         ruleList.add(RULE_WITH_PACKAGE_NAME);
 
-        Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
+        Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
 
         // Verify the resulting map content.
         assertThat(result.keySet())
@@ -157,7 +155,7 @@
         List<Rule> ruleList = new ArrayList();
         ruleList.add(RULE_WITH_APP_CERTIFICATE);
 
-        Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
+        Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
 
         assertThat(result.keySet())
                 .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
@@ -174,7 +172,7 @@
         List<Rule> ruleList = new ArrayList();
         ruleList.add(RULE_WITH_INSTALLER_RESTRICTIONS);
 
-        Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
+        Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
 
         assertThat(result.keySet())
                 .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
@@ -189,7 +187,7 @@
         List<Rule> ruleList = new ArrayList();
         ruleList.add(RULE_WITH_NONSTRING_RESTRICTIONS);
 
-        Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
+        Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
 
         assertThat(result.keySet())
                 .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
@@ -215,7 +213,7 @@
         List<Rule> ruleList = new ArrayList();
         ruleList.add(negatedRule);
 
-        Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
+        Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
 
         assertThat(result.keySet())
                 .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
@@ -225,7 +223,7 @@
     }
 
     @Test
-    public void getIndexType_allRulesTogetherInValidOrder() {
+    public void getIndexType_allRulesTogetherSplitCorrectly() {
         Rule packageNameRuleA = getRuleWithPackageName("aaa");
         Rule packageNameRuleB = getRuleWithPackageName("bbb");
         Rule packageNameRuleC = getRuleWithPackageName("ccc");
@@ -243,38 +241,20 @@
         ruleList.add(RULE_WITH_INSTALLER_RESTRICTIONS);
         ruleList.add(RULE_WITH_NONSTRING_RESTRICTIONS);
 
-        Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
+        Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
 
         assertThat(result.keySet())
                 .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
 
         // We check asserts this way to ensure ordering based on package name.
         assertThat(result.get(PACKAGE_NAME_INDEXED).keySet()).containsExactly("aaa", "bbb", "ccc");
-        Iterator<String> keySetIterator = result.get(PACKAGE_NAME_INDEXED).keySet().iterator();
-        assertThat(keySetIterator.next()).isEqualTo("aaa");
-        assertThat(keySetIterator.next()).isEqualTo("bbb");
-        assertThat(keySetIterator.next()).isEqualTo("ccc");
-        assertThat(result.get(PACKAGE_NAME_INDEXED).get("aaa")).containsExactly(packageNameRuleA);
-        assertThat(result.get(PACKAGE_NAME_INDEXED).get("bbb")).containsExactly(packageNameRuleB);
-        assertThat(result.get(PACKAGE_NAME_INDEXED).get("ccc")).containsExactly(packageNameRuleC);
 
         // We check asserts this way to ensure ordering based on app certificate.
         assertThat(result.get(APP_CERTIFICATE_INDEXED).keySet()).containsExactly("cert1", "cert2",
                 "cert3");
-        keySetIterator = result.get(APP_CERTIFICATE_INDEXED).keySet().iterator();
-        assertThat(keySetIterator.next()).isEqualTo("cert1");
-        assertThat(keySetIterator.next()).isEqualTo("cert2");
-        assertThat(keySetIterator.next()).isEqualTo("cert3");
-        assertThat(result.get(APP_CERTIFICATE_INDEXED).get("cert1")).containsExactly(
-                certificateRule1);
-        assertThat(result.get(APP_CERTIFICATE_INDEXED).get("cert2")).containsExactly(
-                certificateRule2);
-        assertThat(result.get(APP_CERTIFICATE_INDEXED).get("cert3")).containsExactly(
-                certificateRule3);
 
         assertThat(result.get(NOT_INDEXED).get("N/A"))
-                .containsExactly(
-                        RULE_WITH_INSTALLER_RESTRICTIONS,
+                .containsExactly(RULE_WITH_INSTALLER_RESTRICTIONS,
                         RULE_WITH_NONSTRING_RESTRICTIONS);
     }
 
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 c7dbad8..e1c489e 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -37,6 +37,7 @@
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.TAG_ALL;
+import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.NetworkTemplate.buildTemplateWifi;
 import static android.net.TrafficStats.MB_IN_BYTES;
@@ -1758,6 +1759,57 @@
     }
 
     /**
+     * Test that when StatsProvider triggers limit reached, new limit will be calculated and
+     * re-armed.
+     */
+    @Test
+    public void testStatsProviderLimitReached() throws Exception {
+        final int CYCLE_DAY = 15;
+
+        final NetworkStats stats = new NetworkStats(0L, 1);
+        stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
+                2999, 1, 2000, 1, 0);
+        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
+                .thenReturn(stats.getTotalBytes());
+        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
+                .thenReturn(stats);
+
+        // Get active mobile network in place
+        expectMobileDefaults();
+        mService.updateNetworks();
+        verify(mStatsService).setStatsProviderLimit(TEST_IFACE, Long.MAX_VALUE);
+
+        // Set limit to 10KB.
+        setNetworkPolicies(new NetworkPolicy(
+                sTemplateMobileAll, CYCLE_DAY, TIMEZONE_UTC, WARNING_DISABLED, 10000L,
+                true));
+        postMsgAndWaitForCompletion();
+
+        // Verifies that remaining quota is set to providers.
+        verify(mStatsService).setStatsProviderLimit(TEST_IFACE, 10000L - 4999L);
+
+        reset(mStatsService);
+
+        // Increase the usage.
+        stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
+                1000, 1, 999, 1, 0);
+        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
+                .thenReturn(stats.getTotalBytes());
+        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
+                .thenReturn(stats);
+
+        // Simulates that limit reached fires earlier by provider, but actually the quota is not
+        // yet reached.
+        final NetworkPolicyManagerInternal npmi = LocalServices
+                .getService(NetworkPolicyManagerInternal.class);
+        npmi.onStatsProviderLimitReached("TEST");
+
+        // Verifies that the limit reached leads to a force update.
+        postMsgAndWaitForCompletion();
+        verify(mStatsService).forceUpdate();
+    }
+
+    /**
      * Exhaustively test isUidNetworkingBlocked to output the expected results based on external
      * conditions.
      */
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index 0f75816..cc170af 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -37,6 +37,7 @@
 import android.hardware.soundtrigger.V2_3.OptionalModelParameterRange;
 import android.media.audio.common.AudioChannelMask;
 import android.media.audio.common.AudioFormat;
+import android.media.soundtrigger_middleware.AudioCapabilities;
 import android.media.soundtrigger_middleware.ConfidenceLevel;
 import android.media.soundtrigger_middleware.ISoundTriggerCallback;
 import android.media.soundtrigger_middleware.ISoundTriggerModule;
@@ -59,6 +60,7 @@
 import android.os.IHwBinder;
 import android.os.IHwInterface;
 import android.os.RemoteException;
+import android.util.Pair;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -161,6 +163,9 @@
                 new android.hardware.soundtrigger.V2_3.Properties();
         properties.base = createDefaultProperties(supportConcurrentCapture);
         properties.supportedModelArch = "supportedModelArch";
+        properties.audioCapabilities =
+                android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION
+                        | android.hardware.soundtrigger.V2_3.AudioCapabilities.NOISE_SUPPRESSION;
         return properties;
     }
 
@@ -185,8 +190,11 @@
 
         if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
             assertEquals("supportedModelArch", properties.supportedModelArch);
+            assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
+                    properties.audioCapabilities);
         } else {
             assertEquals("", properties.supportedModelArch);
+            assertEquals(0, properties.audioCapabilities);
         }
     }
 
@@ -330,12 +338,17 @@
         mService = new SoundTriggerMiddlewareImpl(mHalDriver, mAudioSessionProvider);
     }
 
-    private int loadGenericModel_2_0(ISoundTriggerModule module, int hwHandle)
-            throws RemoteException {
+    private Pair<Integer, SoundTriggerHwCallback> loadGenericModel_2_0(ISoundTriggerModule module,
+            int hwHandle) throws RemoteException {
         SoundModel model = createGenericSoundModel();
         ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel> modelCaptor =
                 ArgumentCaptor.forClass(
                         android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
+
         doAnswer(invocation -> {
             android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
                     invocation.getArgument(1);
@@ -354,7 +367,8 @@
             modelEvent.model = hwHandle;
             callback.soundModelCallback(modelEvent, callbackCookie);
             return null;
-        }).when(mHalDriver).loadSoundModel(modelCaptor.capture(), any(), anyInt(), any());
+        }).when(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
+                cookieCaptor.capture(), any());
 
         when(mAudioSessionProvider.acquireSession()).thenReturn(
                 new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
@@ -371,17 +385,23 @@
         assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.vendorUuid));
         assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.data.toArray());
 
-        return handle;
+        return new Pair<>(handle,
+                new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
     }
 
-    private int loadGenericModel_2_1(ISoundTriggerModule module, int hwHandle)
-            throws RemoteException {
+    private Pair<Integer, SoundTriggerHwCallback> loadGenericModel_2_1(ISoundTriggerModule module,
+            int hwHandle) throws RemoteException {
         android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
                 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
         SoundModel model = createGenericSoundModel();
         ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
                 ArgumentCaptor.forClass(
                         android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
+
         doAnswer(invocation -> {
             android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
                     invocation.getArgument(1);
@@ -400,7 +420,8 @@
             modelEvent.header.model = hwHandle;
             callback.soundModelCallback_2_1(modelEvent, callbackCookie);
             return null;
-        }).when(driver).loadSoundModel_2_1(modelCaptor.capture(), any(), anyInt(), any());
+        }).when(driver).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
+                cookieCaptor.capture(), any());
 
         when(mAudioSessionProvider.acquireSession()).thenReturn(
                 new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
@@ -418,10 +439,12 @@
         assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
                 HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.data));
 
-        return handle;
+        return new Pair<>(handle,
+                new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
     }
 
-    private int loadGenericModel(ISoundTriggerModule module, int hwHandle) throws RemoteException {
+    private Pair<Integer, SoundTriggerHwCallback> loadGenericModel(ISoundTriggerModule module,
+            int hwHandle) throws RemoteException {
         if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
             return loadGenericModel_2_1(module, hwHandle);
         } else {
@@ -429,12 +452,17 @@
         }
     }
 
-    private int loadPhraseModel_2_0(ISoundTriggerModule module, int hwHandle)
-            throws RemoteException {
+    private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel_2_0(ISoundTriggerModule module,
+            int hwHandle) throws RemoteException {
         PhraseSoundModel model = createPhraseSoundModel();
         ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel>
                 modelCaptor = ArgumentCaptor.forClass(
                 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
+
         doAnswer(invocation -> {
             android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
                     invocation.getArgument(
@@ -456,7 +484,8 @@
             modelEvent.model = hwHandle;
             callback.soundModelCallback(modelEvent, callbackCookie);
             return null;
-        }).when(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), any(), anyInt(), any());
+        }).when(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
+                cookieCaptor.capture(), any());
 
         when(mAudioSessionProvider.acquireSession()).thenReturn(
                 new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
@@ -488,11 +517,12 @@
                         | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
                 hidlPhrase.recognitionModes);
 
-        return handle;
+        return new Pair<>(handle,
+                new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
     }
 
-    private int loadPhraseModel_2_1(ISoundTriggerModule module, int hwHandle)
-            throws RemoteException {
+    private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel_2_1(ISoundTriggerModule module,
+            int hwHandle) throws RemoteException {
         android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
                 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
 
@@ -500,6 +530,11 @@
         ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
                 modelCaptor = ArgumentCaptor.forClass(
                 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
+
         doAnswer(invocation -> {
             android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
                     invocation.getArgument(
@@ -521,7 +556,8 @@
             modelEvent.header.model = hwHandle;
             callback.soundModelCallback_2_1(modelEvent, callbackCookie);
             return null;
-        }).when(driver).loadPhraseSoundModel_2_1(modelCaptor.capture(), any(), anyInt(), any());
+        }).when(driver).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
+                cookieCaptor.capture(), any());
 
         when(mAudioSessionProvider.acquireSession()).thenReturn(
                 new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
@@ -554,10 +590,12 @@
                         | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
                 hidlPhrase.recognitionModes);
 
-        return handle;
+        return new Pair<>(handle,
+                new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
     }
 
-    private int loadPhraseModel(ISoundTriggerModule module, int hwHandle) throws RemoteException {
+    private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel(
+            ISoundTriggerModule module, int hwHandle) throws RemoteException {
         if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
             return loadPhraseModel_2_1(module, hwHandle);
         } else {
@@ -572,18 +610,14 @@
         verify(mAudioSessionProvider).releaseSession(101);
     }
 
-    private SoundTriggerHwCallback startRecognition_2_0(ISoundTriggerModule module, int handle,
+    private void startRecognition_2_0(ISoundTriggerModule module, int handle,
             int hwHandle) throws RemoteException {
         ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig>
                 configCaptor = ArgumentCaptor.forClass(
                 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig.class);
-        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
-                ArgumentCaptor.forClass(
-                        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
-        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
 
-        when(mHalDriver.startRecognition(eq(hwHandle), configCaptor.capture(),
-                callbackCaptor.capture(), cookieCaptor.capture())).thenReturn(0);
+        when(mHalDriver.startRecognition(eq(hwHandle), configCaptor.capture(), any(), anyInt()))
+                .thenReturn(0);
 
         RecognitionConfig config = createRecognitionConfig();
 
@@ -606,11 +640,9 @@
         assertEquals(234, halLevel.userId);
         assertEquals(34, halLevel.levelPercent);
         assertArrayEquals(new Byte[]{5, 4, 3, 2, 1}, halConfig.data.toArray());
-
-        return new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue());
     }
 
-    private SoundTriggerHwCallback startRecognition_2_1(ISoundTriggerModule module, int handle,
+    private void startRecognition_2_1(ISoundTriggerModule module, int handle,
             int hwHandle) throws RemoteException {
         android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
                 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
@@ -618,13 +650,9 @@
         ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig>
                 configCaptor = ArgumentCaptor.forClass(
                 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig.class);
-        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
-                ArgumentCaptor.forClass(
-                        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
-        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
 
-        when(driver.startRecognition_2_1(eq(hwHandle), configCaptor.capture(),
-                callbackCaptor.capture(), cookieCaptor.capture())).thenReturn(0);
+        when(driver.startRecognition_2_1(eq(hwHandle), configCaptor.capture(), any(), anyInt()))
+                .thenReturn(0);
 
         RecognitionConfig config = createRecognitionConfig();
 
@@ -648,16 +676,56 @@
         assertEquals(34, halLevel.levelPercent);
         assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
                 HidlMemoryUtil.hidlMemoryToByteArray(halConfig.data));
-
-        return new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue());
     }
 
-    private SoundTriggerHwCallback startRecognition(ISoundTriggerModule module, int handle,
+    private void startRecognition_2_3(ISoundTriggerModule module, int handle,
             int hwHandle) throws RemoteException {
-        if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
-            return startRecognition_2_1(module, handle, hwHandle);
+        android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+        ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig>
+                configCaptor = ArgumentCaptor.forClass(
+                android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
+
+        when(driver.startRecognition_2_3(eq(hwHandle), configCaptor.capture())).thenReturn(0);
+
+        RecognitionConfig config = createRecognitionConfig();
+
+        module.startRecognition(handle, config);
+        verify(driver).startRecognition_2_3(eq(hwHandle), any());
+
+        android.hardware.soundtrigger.V2_3.RecognitionConfig halConfigExtended =
+                configCaptor.getValue();
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig halConfig_2_1 =
+                halConfigExtended.base;
+
+        assertTrue(halConfig_2_1.header.captureRequested);
+        assertEquals(102, halConfig_2_1.header.captureHandle);
+        assertEquals(103, halConfig_2_1.header.captureDevice);
+        assertEquals(1, halConfig_2_1.header.phrases.size());
+        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
+                halConfig_2_1.header.phrases.get(0);
+        assertEquals(123, halPhraseExtra.id);
+        assertEquals(4, halPhraseExtra.confidenceLevel);
+        assertEquals(5, halPhraseExtra.recognitionModes);
+        assertEquals(1, halPhraseExtra.levels.size());
+        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
+        assertEquals(234, halLevel.userId);
+        assertEquals(34, halLevel.levelPercent);
+        assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
+                HidlMemoryUtil.hidlMemoryToByteArray(halConfig_2_1.data));
+        assertEquals(AudioCapabilities.ECHO_CANCELLATION
+                | AudioCapabilities.NOISE_SUPPRESSION, halConfigExtended.audioCapabilities);
+    }
+
+    private void startRecognition(ISoundTriggerModule module, int handle,
+            int hwHandle) throws RemoteException {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+            startRecognition_2_3(module, handle, hwHandle);
+        } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+            startRecognition_2_1(module, handle, hwHandle);
         } else {
-            return startRecognition_2_0(module, handle, hwHandle);
+            startRecognition_2_0(module, handle, hwHandle);
         }
     }
 
@@ -672,6 +740,8 @@
         config.phraseRecognitionExtras[0].levels[0].userId = 234;
         config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
         config.data = new byte[]{5, 4, 3, 2, 1};
+        config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
+                | AudioCapabilities.NOISE_SUPPRESSION;
         return config;
     }
 
@@ -687,6 +757,9 @@
         if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
             verify((android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver,
                     never()).startRecognition_2_1(anyInt(), any(), any(), anyInt());
+        } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+            verify((android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver,
+                    never()).startRecognition_2_3(anyInt(), any());
         }
     }
 
@@ -798,7 +871,7 @@
         ISoundTriggerModule module = mService.attach(0, callback);
 
         final int hwHandle = 7;
-        int handle = loadGenericModel(module, hwHandle);
+        int handle = loadGenericModel(module, hwHandle).first;
         unloadModel(module, handle, hwHandle);
         module.detach();
     }
@@ -810,7 +883,7 @@
         ISoundTriggerModule module = mService.attach(0, callback);
 
         final int hwHandle = 73;
-        int handle = loadPhraseModel(module, hwHandle);
+        int handle = loadPhraseModel(module, hwHandle).first;
         unloadModel(module, handle, hwHandle);
         module.detach();
     }
@@ -823,7 +896,7 @@
 
         // Load the model.
         final int hwHandle = 7;
-        int handle = loadGenericModel(module, hwHandle);
+        int handle = loadGenericModel(module, hwHandle).first;
 
         // Initiate a recognition.
         startRecognition(module, handle, hwHandle);
@@ -844,7 +917,7 @@
 
         // Load the model.
         final int hwHandle = 67;
-        int handle = loadPhraseModel(module, hwHandle);
+        int handle = loadPhraseModel(module, hwHandle).first;
 
         // Initiate a recognition.
         startRecognition(module, handle, hwHandle);
@@ -865,10 +938,12 @@
 
         // Load the model.
         final int hwHandle = 7;
-        int handle = loadGenericModel(module, hwHandle);
+        Pair<Integer, SoundTriggerHwCallback> modelHandles = loadGenericModel(module, hwHandle);
+        int handle = modelHandles.first;
+        SoundTriggerHwCallback hwCallback = modelHandles.second;
 
         // Initiate a recognition.
-        SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle);
+        startRecognition(module, handle, hwHandle);
 
         // Signal a capture from the driver.
         hwCallback.sendRecognitionEvent(hwHandle,
@@ -894,10 +969,12 @@
 
         // Load the model.
         final int hwHandle = 7;
-        int handle = loadPhraseModel(module, hwHandle);
+        Pair<Integer, SoundTriggerHwCallback> modelHandles = loadPhraseModel(module, hwHandle);
+        int handle = modelHandles.first;
+        SoundTriggerHwCallback hwCallback = modelHandles.second;
 
         // Initiate a recognition.
-        SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle);
+        startRecognition(module, handle, hwHandle);
 
         // Signal a capture from the driver.
         hwCallback.sendPhraseRecognitionEvent(hwHandle,
@@ -930,10 +1007,12 @@
 
         // Load the model.
         final int hwHandle = 17;
-        int handle = loadGenericModel(module, hwHandle);
+        Pair<Integer, SoundTriggerHwCallback> modelHandles = loadGenericModel(module, hwHandle);
+        int handle = modelHandles.first;
+        SoundTriggerHwCallback hwCallback = modelHandles.second;
 
         // Initiate a recognition.
-        SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle);
+        startRecognition(module, handle, hwHandle);
 
         // Force a trigger.
         module.forceRecognitionEvent(handle);
@@ -973,10 +1052,12 @@
 
         // Load the model.
         final int hwHandle = 17;
-        int handle = loadPhraseModel(module, hwHandle);
+        Pair<Integer, SoundTriggerHwCallback> modelHandles = loadPhraseModel(module, hwHandle);
+        int handle = modelHandles.first;
+        SoundTriggerHwCallback hwCallback = modelHandles.second;
 
         // Initiate a recognition.
-        SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle);
+        startRecognition(module, handle, hwHandle);
 
         // Force a trigger.
         module.forceRecognitionEvent(handle);
@@ -1013,7 +1094,7 @@
 
         // Load the model.
         final int hwHandle = 11;
-        int handle = loadGenericModel(module, hwHandle);
+        int handle = loadGenericModel(module, hwHandle).first;
 
         // Initiate a recognition.
         startRecognition(module, handle, hwHandle);
@@ -1061,7 +1142,7 @@
 
         // Load the model.
         final int hwHandle = 11;
-        int handle = loadPhraseModel(module, hwHandle);
+        int handle = loadPhraseModel(module, hwHandle).first;
 
         // Initiate a recognition.
         startRecognition(module, handle, hwHandle);
@@ -1109,7 +1190,7 @@
 
         // Load the model.
         final int hwHandle = 13;
-        int handle = loadGenericModel(module, hwHandle);
+        int handle = loadGenericModel(module, hwHandle).first;
 
         // Initiate a recognition.
         startRecognition(module, handle, hwHandle);
@@ -1145,7 +1226,7 @@
 
         // Load the model.
         final int hwHandle = 13;
-        int handle = loadPhraseModel(module, hwHandle);
+        int handle = loadPhraseModel(module, hwHandle).first;
 
         // Initiate a recognition.
         startRecognition(module, handle, hwHandle);
@@ -1182,7 +1263,7 @@
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
         final int hwHandle = 12;
-        int modelHandle = loadGenericModel(module, hwHandle);
+        int modelHandle = loadGenericModel(module, hwHandle).first;
 
         doAnswer((Answer<Void>) invocation -> {
             android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
@@ -1218,7 +1299,7 @@
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
         final int hwHandle = 13;
-        int modelHandle = loadGenericModel(module, hwHandle);
+        int modelHandle = loadGenericModel(module, hwHandle).first;
 
         ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
                 ModelParameter.THRESHOLD_FACTOR);
@@ -1239,7 +1320,7 @@
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
         final int hwHandle = 13;
-        int modelHandle = loadGenericModel(module, hwHandle);
+        int modelHandle = loadGenericModel(module, hwHandle).first;
 
         doAnswer(invocation -> {
             android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
@@ -1272,7 +1353,7 @@
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
         final int hwHandle = 14;
-        int modelHandle = loadGenericModel(module, hwHandle);
+        int modelHandle = loadGenericModel(module, hwHandle).first;
 
         doAnswer(invocation -> {
             android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getParameterCallback
@@ -1304,7 +1385,7 @@
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
         final int hwHandle = 17;
-        int modelHandle = loadGenericModel(module, hwHandle);
+        int modelHandle = loadGenericModel(module, hwHandle).first;
 
         when(driver.setParameter(hwHandle,
                 android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR,
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 aaf9799..8a3183f 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -18,7 +18,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -26,7 +25,6 @@
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.PhoneTimeSuggestion;
-import android.content.Intent;
 import android.icu.util.Calendar;
 import android.icu.util.GregorianCalendar;
 import android.icu.util.TimeZone;
@@ -78,8 +76,7 @@
 
         long expectedSystemClockMillis =
                 mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
-        mScript.verifySystemClockWasSetAndResetCallTracking(
-                expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+        mScript.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis)
                 .assertLatestPhoneSuggestion(phoneId, timeSuggestion);
     }
 
@@ -118,8 +115,7 @@
                     mScript.calculateTimeInMillisForNow(timeSuggestion1.getUtcTime());
 
             mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
-                    .verifySystemClockWasSetAndResetCallTracking(
-                            expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
+                    .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1)
                     .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
         }
 
@@ -146,8 +142,7 @@
                     mScript.calculateTimeInMillisForNow(timeSuggestion3.getUtcTime());
 
             mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
-                    .verifySystemClockWasSetAndResetCallTracking(
-                            expectedSystemClockMillis3, true /* expectNetworkBroadcast */)
+                    .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis3)
                     .assertLatestPhoneSuggestion(phoneId, timeSuggestion3);
         }
     }
@@ -175,8 +170,7 @@
                     mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
 
             mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
-                    .verifySystemClockWasSetAndResetCallTracking(
-                            expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+                    .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis)
                     .assertLatestPhoneSuggestion(phone1Id, null)
                     .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
         }
@@ -193,8 +187,7 @@
                     mScript.calculateTimeInMillisForNow(phone1TimeSuggestion.getUtcTime());
 
             mScript.simulatePhoneTimeSuggestion(phone1TimeSuggestion)
-                    .verifySystemClockWasSetAndResetCallTracking(
-                            expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+                    .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis)
                     .assertLatestPhoneSuggestion(phone1Id, phone1TimeSuggestion);
 
         }
@@ -227,8 +220,7 @@
                     mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
 
             mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
-                    .verifySystemClockWasSetAndResetCallTracking(
-                            expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+                    .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis)
                     .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
         }
     }
@@ -265,8 +257,7 @@
         mScript.simulateTimePassing();
         long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1);
         mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
+                .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1)
                 .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
 
         // The UTC time increment should be larger than the system clock update threshold so we
@@ -304,8 +295,7 @@
         PhoneTimeSuggestion timeSuggestion4 =
                 createPhoneTimeSuggestion(phoneId, utcTime4);
         mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis4, true /* expectNetworkBroadcast */)
+                .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4)
                 .assertLatestPhoneSuggestion(phoneId, timeSuggestion4);
     }
 
@@ -339,8 +329,7 @@
 
         // Turn on auto time detection.
         mScript.simulateAutoTimeDetectionToggle()
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
+                .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1)
                 .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
 
         // Turn off auto time detection.
@@ -367,8 +356,7 @@
 
         // Turn on auto time detection.
         mScript.simulateAutoTimeDetectionToggle()
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis2, true /* expectNetworkBroadcast */)
+                .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2)
                 .assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
     }
 
@@ -388,7 +376,7 @@
                 mScript.calculateTimeInMillisForNow(phoneSuggestion.getUtcTime());
         mScript.simulatePhoneTimeSuggestion(phoneSuggestion)
                 .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis, true /* expectedNetworkBroadcast */)
+                        expectedSystemClockMillis  /* expectedNetworkBroadcast */)
                 .assertLatestPhoneSuggestion(phoneId, phoneSuggestion);
 
         // Look inside and check what the strategy considers the current best phone suggestion.
@@ -416,8 +404,7 @@
         long expectedSystemClockMillis =
                 mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
         mScript.simulateManualTimeSuggestion(timeSuggestion)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis, false /* expectNetworkBroadcast */);
+                .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis);
     }
 
     @Test
@@ -439,8 +426,7 @@
         long expectedAutoClockMillis =
                 mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
         mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedAutoClockMillis, true /* expectNetworkBroadcast */)
+                .verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis)
                 .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
 
         // Simulate the passage of time.
@@ -463,8 +449,7 @@
         long expectedManualClockMillis =
                 mScript.calculateTimeInMillisForNow(manualTimeSuggestion.getUtcTime());
         mScript.simulateManualTimeSuggestion(manualTimeSuggestion)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedManualClockMillis, false /* expectNetworkBroadcast */)
+                .verifySystemClockWasSetAndResetCallTracking(expectedManualClockMillis)
                 .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
 
         // Simulate the passage of time.
@@ -475,8 +460,7 @@
 
         expectedAutoClockMillis =
                 mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
-        mScript.verifySystemClockWasSetAndResetCallTracking(
-                        expectedAutoClockMillis, true /* expectNetworkBroadcast */)
+        mScript.verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis)
                 .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
 
         // Switch back to manual - nothing should happen to the clock.
@@ -514,8 +498,7 @@
         long expectedSystemClockMillis =
                 mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
         mScript.simulateNetworkTimeSuggestion(timeSuggestion)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis, false /* expectNetworkBroadcast */);
+                .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis);
     }
 
     @Test
@@ -550,8 +533,7 @@
         mScript.simulateTimePassing(smallTimeIncrementMillis)
                 .simulateNetworkTimeSuggestion(networkTimeSuggestion1)
                 .verifySystemClockWasSetAndResetCallTracking(
-                        mScript.calculateTimeInMillisForNow(networkTimeSuggestion1.getUtcTime()),
-                        false /* expectNetworkBroadcast */);
+                        mScript.calculateTimeInMillisForNow(networkTimeSuggestion1.getUtcTime()));
 
         // Check internal state.
         mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, null)
@@ -570,8 +552,7 @@
         mScript.simulateTimePassing(smallTimeIncrementMillis)
                 .simulatePhoneTimeSuggestion(phoneTimeSuggestion)
                 .verifySystemClockWasSetAndResetCallTracking(
-                        mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()),
-                        true /* expectNetworkBroadcast */);
+                        mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()));
 
         // Check internal state.
         mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
@@ -622,8 +603,7 @@
 
         // Verify the latest network time now wins.
         mScript.verifySystemClockWasSetAndResetCallTracking(
-                mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUtcTime()),
-                false /* expectNetworkTimeBroadcast */);
+                mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUtcTime()));
 
         // Check internal state.
         mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
@@ -645,7 +625,6 @@
 
         // Tracking operations.
         private boolean mSystemClockWasSet;
-        private Intent mBroadcastSent;
 
         @Override
         public int systemClockUpdateThresholdMillis() {
@@ -689,12 +668,6 @@
             mWakeLockAcquired = false;
         }
 
-        @Override
-        public void sendStickyBroadcast(Intent intent) {
-            assertNotNull(intent);
-            mBroadcastSent = intent;
-        }
-
         // Methods below are for managing the fake's behavior.
 
         void pokeSystemClockUpdateThreshold(int thresholdMillis) {
@@ -739,17 +712,8 @@
             assertEquals(expectedSystemClockMillis, mSystemClockMillis);
         }
 
-        void verifyIntentWasBroadcast() {
-            assertTrue(mBroadcastSent != null);
-        }
-
-        void verifyIntentWasNotBroadcast() {
-            assertNull(mBroadcastSent);
-        }
-
         void resetCallTracking() {
             mSystemClockWasSet = false;
-            mBroadcastSent = null;
         }
 
         private void assertWakeLockAcquired() {
@@ -832,17 +796,12 @@
 
         Script verifySystemClockWasNotSetAndResetCallTracking() {
             mFakeCallback.verifySystemClockNotSet();
-            mFakeCallback.verifyIntentWasNotBroadcast();
             mFakeCallback.resetCallTracking();
             return this;
         }
 
-        Script verifySystemClockWasSetAndResetCallTracking(
-                long expectedSystemClockMillis, boolean expectNetworkBroadcast) {
+        Script verifySystemClockWasSetAndResetCallTracking(long expectedSystemClockMillis) {
             mFakeCallback.verifySystemClockWasSet(expectedSystemClockMillis);
-            if (expectNetworkBroadcast) {
-                mFakeCallback.verifyIntentWasBroadcast();
-            }
             mFakeCallback.resetCallTracking();
             return this;
         }
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
deleted file mode 100644
index 9cda084..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.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/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java
index f7cd6a3..9906585 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java
@@ -21,12 +21,10 @@
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
-import android.hardware.soundtrigger.SoundTrigger;
 import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
-import android.text.TextUtils;
 import android.util.Slog;
 
-import java.util.Locale;
+import java.io.PrintWriter;
 import java.util.UUID;
 
 /**
@@ -39,7 +37,7 @@
     static final boolean DBG = false;
 
     private static final String NAME = "st_sound_model.db";
-    private static final int VERSION = 1;
+    private static final int VERSION = 2;
 
     // Sound trigger-based sound models.
     public static interface GenericSoundModelContract {
@@ -47,15 +45,16 @@
         public static final String KEY_MODEL_UUID = "model_uuid";
         public static final String KEY_VENDOR_UUID = "vendor_uuid";
         public static final String KEY_DATA = "data";
+        public static final String KEY_MODEL_VERSION = "model_version";
     }
 
-
     // Table Create Statement for the sound trigger table
     private static final String CREATE_TABLE_ST_SOUND_MODEL = "CREATE TABLE "
             + GenericSoundModelContract.TABLE + "("
             + GenericSoundModelContract.KEY_MODEL_UUID + " TEXT PRIMARY KEY,"
             + GenericSoundModelContract.KEY_VENDOR_UUID + " TEXT,"
-            + GenericSoundModelContract.KEY_DATA + " BLOB" + " )";
+            + GenericSoundModelContract.KEY_DATA + " BLOB,"
+            + GenericSoundModelContract.KEY_MODEL_VERSION + " INTEGER" + " )";
 
 
     public SoundTriggerDbHelper(Context context) {
@@ -70,9 +69,13 @@
 
     @Override
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-        // TODO: For now, drop older tables and recreate new ones.
-        db.execSQL("DROP TABLE IF EXISTS " + GenericSoundModelContract.TABLE);
-        onCreate(db);
+        if (oldVersion == 1) {
+            // In version 2, a model version number was added.
+            Slog.d(TAG, "Adding model version column");
+            db.execSQL("ALTER TABLE " + GenericSoundModelContract.TABLE + " ADD COLUMN "
+                    + GenericSoundModelContract.KEY_MODEL_VERSION + " INTEGER DEFAULT -1");
+            oldVersion++;
+        }
     }
 
     /**
@@ -86,6 +89,7 @@
             values.put(GenericSoundModelContract.KEY_MODEL_UUID, soundModel.uuid.toString());
             values.put(GenericSoundModelContract.KEY_VENDOR_UUID, soundModel.vendorUuid.toString());
             values.put(GenericSoundModelContract.KEY_DATA, soundModel.data);
+            values.put(GenericSoundModelContract.KEY_MODEL_VERSION, soundModel.version);
 
             try {
                 return db.insertWithOnConflict(GenericSoundModelContract.TABLE, null, values,
@@ -113,8 +117,10 @@
                                 GenericSoundModelContract.KEY_DATA));
                         String vendor_uuid = c.getString(
                                 c.getColumnIndex(GenericSoundModelContract.KEY_VENDOR_UUID));
+                        int version = c.getInt(
+                                c.getColumnIndex(GenericSoundModelContract.KEY_MODEL_VERSION));
                         return new GenericSoundModel(model_uuid, UUID.fromString(vendor_uuid),
-                                data);
+                                data, version);
                     } while (c.moveToNext());
                 }
             } finally {
@@ -142,4 +148,48 @@
             }
         }
     }
+
+    public void dump(PrintWriter pw) {
+        synchronized(this) {
+            String selectQuery = "SELECT  * FROM " + GenericSoundModelContract.TABLE;
+            SQLiteDatabase db = getReadableDatabase();
+            Cursor c = db.rawQuery(selectQuery, null);
+            try {
+                pw.println("  Enrolled GenericSoundModels:");
+                if (c.moveToFirst()) {
+                    String[] columnNames = c.getColumnNames();
+                    do {
+                        for (String name : columnNames) {
+                            int colNameIndex = c.getColumnIndex(name);
+                            int type = c.getType(colNameIndex);
+                            switch (type) {
+                                case Cursor.FIELD_TYPE_STRING:
+                                    pw.printf("    %s: %s\n", name,
+                                            c.getString(colNameIndex));
+                                    break;
+                                case Cursor.FIELD_TYPE_BLOB:
+                                    pw.printf("    %s: data blob\n", name);
+                                    break;
+                                case Cursor.FIELD_TYPE_INTEGER:
+                                    pw.printf("    %s: %d\n", name,
+                                            c.getInt(colNameIndex));
+                                    break;
+                                case Cursor.FIELD_TYPE_FLOAT:
+                                    pw.printf("    %s: %f\n", name,
+                                            c.getFloat(colNameIndex));
+                                    break;
+                                case Cursor.FIELD_TYPE_NULL:
+                                    pw.printf("    %s: null\n", name);
+                                    break;
+                            }
+                        }
+                        pw.println();
+                    } while (c.moveToNext());
+                }
+            } finally {
+                c.close();
+                db.close();
+            }
+        }
+    }
 }
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index e37755b..767010a 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -1495,6 +1495,9 @@
             // log
             sEventLogger.dump(pw);
 
+            // enrolled models
+            mDbHelper.dump(pw);
+
             // stats
             mSoundModelStatTracker.dump(pw);
         }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index dd7b5a8..c58b6da 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -27,6 +27,7 @@
 import android.text.TextUtils;
 import android.util.Slog;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -43,7 +44,7 @@
     static final boolean DBG = false;
 
     private static final String NAME = "sound_model.db";
-    private static final int VERSION = 6;
+    private static final int VERSION = 7;
 
     public static interface SoundModelContract {
         public static final String TABLE = "sound_model";
@@ -56,6 +57,7 @@
         public static final String KEY_LOCALE = "locale";
         public static final String KEY_HINT_TEXT = "hint_text";
         public static final String KEY_USERS = "users";
+        public static final String KEY_MODEL_VERSION = "model_version";
     }
 
     // Table Create Statement
@@ -70,6 +72,7 @@
             + SoundModelContract.KEY_LOCALE + " TEXT,"
             + SoundModelContract.KEY_HINT_TEXT + " TEXT,"
             + SoundModelContract.KEY_USERS + " TEXT,"
+            + SoundModelContract.KEY_MODEL_VERSION + " INTEGER,"
             + "PRIMARY KEY (" + SoundModelContract.KEY_KEYPHRASE_ID + ","
                               + SoundModelContract.KEY_LOCALE + ","
                               + SoundModelContract.KEY_USERS + ")"
@@ -138,6 +141,13 @@
             }
             oldVersion++;
         }
+        if (oldVersion == 6) {
+            // In version 7, a model version number was added.
+            Slog.d(TAG, "Adding model version column");
+            db.execSQL("ALTER TABLE " + SoundModelContract.TABLE + " ADD COLUMN "
+                    + SoundModelContract.KEY_MODEL_VERSION + " INTEGER DEFAULT -1");
+            oldVersion++;
+        }
     }
 
     /**
@@ -155,6 +165,7 @@
             }
             values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
             values.put(SoundModelContract.KEY_DATA, soundModel.data);
+            values.put(SoundModelContract.KEY_MODEL_VERSION, soundModel.version);
 
             if (soundModel.keyphrases != null && soundModel.keyphrases.length == 1) {
                 values.put(SoundModelContract.KEY_KEYPHRASE_ID, soundModel.keyphrases[0].id);
@@ -250,6 +261,8 @@
                                 c.getColumnIndex(SoundModelContract.KEY_LOCALE));
                         String text = c.getString(
                                 c.getColumnIndex(SoundModelContract.KEY_HINT_TEXT));
+                        int version = c.getInt(
+                                c.getColumnIndex(SoundModelContract.KEY_MODEL_VERSION));
 
                         // Only add keyphrases meant for the current user.
                         if (users == null) {
@@ -282,7 +295,7 @@
                             vendorUuid = UUID.fromString(vendorUuidString);
                         }
                         KeyphraseSoundModel model = new KeyphraseSoundModel(
-                                UUID.fromString(modelUuid), vendorUuid, data, keyphrases);
+                                UUID.fromString(modelUuid), vendorUuid, data, keyphrases, version);
                         if (DBG) {
                             Slog.d(TAG, "Found SoundModel for the given keyphrase/locale/user: "
                                     + model);
@@ -325,6 +338,10 @@
         return users;
     }
 
+    /**
+     * SoundModelRecord is no longer used, and it should only be used on database migration.
+     * This class does not need to be modified when modifying the database scheme.
+     */
     private static class SoundModelRecord {
         public final String modelUuid;
         public final String vendorUuid;
@@ -413,4 +430,48 @@
           return a == b;
         }
     }
+
+    public void dump(PrintWriter pw) {
+        synchronized(this) {
+            String selectQuery = "SELECT  * FROM " + SoundModelContract.TABLE;
+            SQLiteDatabase db = getReadableDatabase();
+            Cursor c = db.rawQuery(selectQuery, null);
+            try {
+                pw.println("  Enrolled KeyphraseSoundModels:");
+                if (c.moveToFirst()) {
+                    String[] columnNames = c.getColumnNames();
+                    do {
+                        for (String name : columnNames) {
+                            int colNameIndex = c.getColumnIndex(name);
+                            int type = c.getType(colNameIndex);
+                            switch (type) {
+                                case Cursor.FIELD_TYPE_STRING:
+                                    pw.printf("    %s: %s\n", name,
+                                            c.getString(colNameIndex));
+                                    break;
+                                case Cursor.FIELD_TYPE_BLOB:
+                                    pw.printf("    %s: data blob\n", name);
+                                    break;
+                                case Cursor.FIELD_TYPE_INTEGER:
+                                    pw.printf("    %s: %d\n", name,
+                                            c.getInt(colNameIndex));
+                                    break;
+                                case Cursor.FIELD_TYPE_FLOAT:
+                                    pw.printf("    %s: %f\n", name,
+                                            c.getFloat(colNameIndex));
+                                    break;
+                                case Cursor.FIELD_TYPE_NULL:
+                                    pw.printf("    %s: null\n", name);
+                                    break;
+                            }
+                        }
+                        pw.println();
+                    } while (c.moveToNext());
+                }
+            } finally {
+                c.close();
+                db.close();
+            }
+        }
+    }
 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 06c8074..506c67e 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -159,9 +159,13 @@
         }
     }
 
+    private boolean isSupported(UserInfo user) {
+        return user.isFull();
+    }
+
     @Override
-    public boolean isSupported(UserInfo userInfo) {
-        return userInfo.isFull();
+    public boolean isSupportedUser(TargetUser user) {
+        return isSupported(user.getUserInfo());
     }
 
     @Override
@@ -1343,6 +1347,7 @@
                 pw.println("  mCurUserUnlocked: " + mCurUserUnlocked);
                 pw.println("  mCurUserSupported: " + mCurUserSupported);
                 dumpSupportedUsers(pw, "  ");
+                mDbHelper.dump(pw);
                 if (mImpl == null) {
                     pw.println("  (No active implementation)");
                     return;
diff --git a/telecomm/TEST_MAPPING b/telecomm/TEST_MAPPING
new file mode 100644
index 0000000..d585666
--- /dev/null
+++ b/telecomm/TEST_MAPPING
@@ -0,0 +1,29 @@
+{
+  "presubmit": [
+    {
+      "name": "TeleServiceTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "TelecomUnitTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "TelephonyProviderTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/telephony/TEST_MAPPING b/telephony/TEST_MAPPING
new file mode 100644
index 0000000..d585666
--- /dev/null
+++ b/telephony/TEST_MAPPING
@@ -0,0 +1,29 @@
+{
+  "presubmit": [
+    {
+      "name": "TeleServiceTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "TelecomUnitTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "TelephonyProviderTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index aaafee2..f39981f 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -16,8 +16,6 @@
 
 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 97bcbc0..9bc534c 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -27,10 +27,10 @@
 import android.os.UserHandle;
 import android.permission.IPermissionManager;
 import android.provider.Settings;
-import com.android.telephony.Rlog;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -80,7 +80,7 @@
             IPackageManager packageManager, IPermissionManager permissionManager,
             TelephonyManager telephonyManager, int userId, Context context) {
         if (DEBUG) {
-            Rlog.d(TAG, "disableCarrierAppsUntilPrivileged");
+            Log.d(TAG, "disableCarrierAppsUntilPrivileged");
         }
         SystemConfig config = SystemConfig.getInstance();
         ArraySet<String> systemCarrierAppsDisabledUntilUsed =
@@ -108,7 +108,7 @@
             IPackageManager packageManager, IPermissionManager permissionManager, int userId,
             Context context) {
         if (DEBUG) {
-            Rlog.d(TAG, "disableCarrierAppsUntilPrivileged");
+            Log.d(TAG, "disableCarrierAppsUntilPrivileged");
         }
         SystemConfig config = SystemConfig.getInstance();
         ArraySet<String> systemCarrierAppsDisabledUntilUsed =
@@ -141,8 +141,8 @@
             ContentResolver contentResolver, int userId,
             ArraySet<String> systemCarrierAppsDisabledUntilUsed,
             ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed) {
-        List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager,
-                userId, systemCarrierAppsDisabledUntilUsed);
+        List<ApplicationInfo> candidates = getDefaultNotUpdatedCarrierAppCandidatesHelper(
+                packageManager, userId, systemCarrierAppsDisabledUntilUsed);
         if (candidates == null || candidates.isEmpty()) {
             return;
         }
@@ -178,16 +178,17 @@
                     }
                 }
 
+                int enabledSetting = packageManager.getApplicationEnabledSetting(packageName,
+                        userId);
                 if (hasPrivileges) {
                     // Only update enabled state for the app on /system. Once it has been
                     // updated we shouldn't touch it.
-                    if (!ai.isUpdatedSystemApp()
-                            && (ai.enabledSetting
+                    if (enabledSetting
                             == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
-                            || ai.enabledSetting
+                            || enabledSetting
                             == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
-                            || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0)) {
-                        Rlog.i(TAG, "Update state(" + packageName + "): ENABLED for user "
+                            || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+                        Log.i(TAG, "Update state(" + packageName + "): ENABLED for user "
                                 + userId);
                         packageManager.setSystemAppInstallState(
                                 packageName,
@@ -204,13 +205,16 @@
                     // Also enable any associated apps for this carrier app.
                     if (associatedAppList != null) {
                         for (ApplicationInfo associatedApp : associatedAppList) {
-                            if (associatedApp.enabledSetting
+                            int associatedAppEnabledSetting =
+                                    packageManager.getApplicationEnabledSetting(
+                                    associatedApp.packageName, userId);
+                            if (associatedAppEnabledSetting
                                     == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
-                                    || associatedApp.enabledSetting
+                                    || associatedAppEnabledSetting
                                     == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
                                     || (associatedApp.flags
                                     & ApplicationInfo.FLAG_INSTALLED) == 0) {
-                                Rlog.i(TAG, "Update associated state(" + associatedApp.packageName
+                                Log.i(TAG, "Update associated state(" + associatedApp.packageName
                                         + "): ENABLED for user " + userId);
                                 packageManager.setSystemAppInstallState(
                                         associatedApp.packageName,
@@ -231,11 +235,10 @@
                 } else {  // No carrier privileges
                     // Only update enabled state for the app on /system. Once it has been
                     // updated we shouldn't touch it.
-                    if (!ai.isUpdatedSystemApp()
-                            && ai.enabledSetting
+                    if (enabledSetting
                             == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                             && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
-                        Rlog.i(TAG, "Update state(" + packageName
+                        Log.i(TAG, "Update state(" + packageName
                                 + "): DISABLED_UNTIL_USED for user " + userId);
                         packageManager.setSystemAppInstallState(
                                 packageName,
@@ -249,11 +252,14 @@
                     if (!hasRunOnce) {
                         if (associatedAppList != null) {
                             for (ApplicationInfo associatedApp : associatedAppList) {
-                                if (associatedApp.enabledSetting
+                                int associatedAppEnabledSetting =
+                                        packageManager.getApplicationEnabledSetting(
+                                        associatedApp.packageName, userId);
+                                if (associatedAppEnabledSetting
                                         == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                                         && (associatedApp.flags
                                         & ApplicationInfo.FLAG_INSTALLED) != 0) {
-                                    Rlog.i(TAG,
+                                    Log.i(TAG,
                                             "Update associated state(" + associatedApp.packageName
                                                     + "): DISABLED_UNTIL_USED for user " + userId);
                                     packageManager.setSystemAppInstallState(
@@ -280,7 +286,7 @@
                 permissionManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId);
             }
         } catch (RemoteException e) {
-            Rlog.w(TAG, "Could not reach PackageManager", e);
+            Log.w(TAG, "Could not reach PackageManager", e);
         }
     }
 
@@ -360,6 +366,31 @@
         return apps;
     }
 
+    private static List<ApplicationInfo> getDefaultNotUpdatedCarrierAppCandidatesHelper(
+            IPackageManager packageManager,
+            int userId,
+            ArraySet<String> systemCarrierAppsDisabledUntilUsed) {
+        if (systemCarrierAppsDisabledUntilUsed == null) {
+            return null;
+        }
+
+        int size = systemCarrierAppsDisabledUntilUsed.size();
+        if (size == 0) {
+            return null;
+        }
+
+        List<ApplicationInfo> apps = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            String packageName = systemCarrierAppsDisabledUntilUsed.valueAt(i);
+            ApplicationInfo ai =
+                    getApplicationInfoIfNotUpdatedSystemApp(packageManager, userId, packageName);
+            if (ai != null) {
+                apps.add(ai);
+            }
+        }
+        return apps;
+    }
+
     private static Map<String, List<ApplicationInfo>> getDefaultCarrierAssociatedAppsHelper(
             IPackageManager packageManager,
             int userId,
@@ -372,11 +403,11 @@
                     systemCarrierAssociatedAppsDisabledUntilUsed.valueAt(i);
             for (int j = 0; j < associatedAppPackages.size(); j++) {
                 ApplicationInfo ai =
-                        getApplicationInfoIfSystemApp(
+                        getApplicationInfoIfNotUpdatedSystemApp(
                                 packageManager, userId, associatedAppPackages.get(j));
                 // Only update enabled state for the app on /system. Once it has been updated we
                 // shouldn't touch it.
-                if (ai != null && !ai.isUpdatedSystemApp()) {
+                if (ai != null) {
                     List<ApplicationInfo> appList = associatedApps.get(carrierAppPackage);
                     if (appList == null) {
                         appList = new ArrayList<>();
@@ -390,6 +421,26 @@
     }
 
     @Nullable
+    private static ApplicationInfo getApplicationInfoIfNotUpdatedSystemApp(
+            IPackageManager packageManager,
+            int userId,
+            String packageName) {
+        try {
+            ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
+                    PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                            | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
+                            | PackageManager.MATCH_SYSTEM_ONLY
+                            | PackageManager.MATCH_FACTORY_ONLY, userId);
+            if (ai != null) {
+                return ai;
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Could not reach PackageManager", e);
+        }
+        return null;
+    }
+
+    @Nullable
     private static ApplicationInfo getApplicationInfoIfSystemApp(
             IPackageManager packageManager,
             int userId,
@@ -397,12 +448,13 @@
         try {
             ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
                     PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
-                    | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, userId);
-            if (ai != null && ai.isSystemApp()) {
+                    | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
+                    | PackageManager.MATCH_SYSTEM_ONLY, userId);
+            if (ai != null) {
                 return ai;
             }
         } catch (RemoteException e) {
-            Rlog.w(TAG, "Could not reach PackageManager", e);
+            Log.w(TAG, "Could not reach PackageManager", e);
         }
         return null;
     }
diff --git a/telephony/common/com/android/internal/telephony/GsmAlphabet.java b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
index a36ff93..5c53f7e 100644
--- a/telephony/common/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
@@ -19,7 +19,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.os.Build;
-import com.android.telephony.Rlog;
+import android.util.Log;
 import android.text.TextUtils;
 import android.util.SparseIntArray;
 
@@ -498,11 +498,11 @@
         StringBuilder ret = new StringBuilder(lengthSeptets);
 
         if (languageTable < 0 || languageTable > sLanguageTables.length) {
-            Rlog.w(TAG, "unknown language table " + languageTable + ", using default");
+            Log.w(TAG, "unknown language table " + languageTable + ", using default");
             languageTable = 0;
         }
         if (shiftTable < 0 || shiftTable > sLanguageShiftTables.length) {
-            Rlog.w(TAG, "unknown single shift table " + shiftTable + ", using default");
+            Log.w(TAG, "unknown single shift table " + shiftTable + ", using default");
             shiftTable = 0;
         }
 
@@ -512,11 +512,11 @@
             String shiftTableToChar = sLanguageShiftTables[shiftTable];
 
             if (languageTableToChar.isEmpty()) {
-                Rlog.w(TAG, "no language table for code " + languageTable + ", using default");
+                Log.w(TAG, "no language table for code " + languageTable + ", using default");
                 languageTableToChar = sLanguageTables[0];
             }
             if (shiftTableToChar.isEmpty()) {
-                Rlog.w(TAG, "no single shift table for code " + shiftTable + ", using default");
+                Log.w(TAG, "no single shift table for code " + shiftTable + ", using default");
                 shiftTableToChar = sLanguageShiftTables[0];
             }
 
@@ -556,7 +556,7 @@
                 }
             }
         } catch (RuntimeException ex) {
-            Rlog.e(TAG, "Error GSM 7 bit packed: ", ex);
+            Log.e(TAG, "Error GSM 7 bit packed: ", ex);
             return null;
         }
 
@@ -813,7 +813,7 @@
         for (int i = 0; i < sz; i++) {
             char c = s.charAt(i);
             if (c == GSM_EXTENDED_ESCAPE) {
-                Rlog.w(TAG, "countGsmSeptets() string contains Escape character, skipping.");
+                Log.w(TAG, "countGsmSeptets() string contains Escape character, skipping.");
                 continue;
             }
             if (charToLanguageTable.get(c, -1) != -1) {
@@ -892,7 +892,7 @@
         for (int i = 0; i < sz && !lpcList.isEmpty(); i++) {
             char c = s.charAt(i);
             if (c == GSM_EXTENDED_ESCAPE) {
-                Rlog.w(TAG, "countGsmSeptets() string contains Escape character, ignoring!");
+                Log.w(TAG, "countGsmSeptets() string contains Escape character, ignoring!");
                 continue;
             }
             // iterate through enabled locking shift tables
@@ -1496,7 +1496,7 @@
         int numTables = sLanguageTables.length;
         int numShiftTables = sLanguageShiftTables.length;
         if (numTables != numShiftTables) {
-            Rlog.e(TAG, "Error: language tables array length " + numTables +
+            Log.e(TAG, "Error: language tables array length " + numTables +
                     " != shift tables array length " + numShiftTables);
         }
 
@@ -1506,7 +1506,7 @@
 
             int tableLen = table.length();
             if (tableLen != 0 && tableLen != 128) {
-                Rlog.e(TAG, "Error: language tables index " + i +
+                Log.e(TAG, "Error: language tables index " + i +
                         " length " + tableLen + " (expected 128 or 0)");
             }
 
@@ -1524,7 +1524,7 @@
 
             int shiftTableLen = shiftTable.length();
             if (shiftTableLen != 0 && shiftTableLen != 128) {
-                Rlog.e(TAG, "Error: language shift tables index " + i +
+                Log.e(TAG, "Error: language shift tables index " + i +
                         " length " + shiftTableLen + " (expected 128 or 0)");
             }
 
diff --git a/telephony/common/com/android/internal/telephony/HbpcdUtils.java b/telephony/common/com/android/internal/telephony/HbpcdUtils.java
index 45a563c..714f5a6 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 com.android.telephony.Rlog;
+import android.util.Log;
 
 import com.android.internal.telephony.HbpcdLookup.ArbitraryMccSidMatch;
 import com.android.internal.telephony.HbpcdLookup.MccIdd;
@@ -54,16 +54,16 @@
         if (c2 != null) {
             int c2Counter = c2.getCount();
             if (DBG) {
-                Rlog.d(LOG_TAG, "Query unresolved arbitrary table, entries are " + c2Counter);
+                Log.d(LOG_TAG, "Query unresolved arbitrary table, entries are " + c2Counter);
             }
             if (c2Counter == 1) {
                 if (DBG) {
-                    Rlog.d(LOG_TAG, "Query Unresolved arbitrary returned the cursor " + c2);
+                    Log.d(LOG_TAG, "Query Unresolved arbitrary returned the cursor " + c2);
                 }
                 c2.moveToFirst();
                 tmpMcc = c2.getInt(0);
                 if (DBG) {
-                    Rlog.d(LOG_TAG, "MCC found in arbitrary_mcc_sid_match: " + tmpMcc);
+                    Log.d(LOG_TAG, "MCC found in arbitrary_mcc_sid_match: " + tmpMcc);
                 }
                 c2.close();
                 return tmpMcc;
@@ -85,18 +85,18 @@
             int c3Counter = c3.getCount();
             if (c3Counter > 0) {
                 if (c3Counter > 1) {
-                    Rlog.w(LOG_TAG, "something wrong, get more results for 1 conflict SID: " + c3);
+                    Log.w(LOG_TAG, "something wrong, get more results for 1 conflict SID: " + c3);
                 }
-                if (DBG) Rlog.d(LOG_TAG, "Query conflict sid returned the cursor " + c3);
+                if (DBG) Log.d(LOG_TAG, "Query conflict sid returned the cursor " + c3);
                 c3.moveToFirst();
                 tmpMcc = c3.getInt(0);
                 if (DBG) {
-                    Rlog.d(LOG_TAG, "MCC found in mcc_lookup_table. Return tmpMcc = " + tmpMcc);
+                    Log.d(LOG_TAG, "MCC found in mcc_lookup_table. Return tmpMcc = " + tmpMcc);
                 }
                 if (!isNitzTimeZone) {
                     // time zone is not accurate, it may get wrong mcc, ignore it.
                     if (DBG) {
-                        Rlog.d(LOG_TAG, "time zone is not accurate, mcc may be " + tmpMcc);
+                        Log.d(LOG_TAG, "time zone is not accurate, mcc may be " + tmpMcc);
                     }
                     tmpMcc = 0;
                 }
@@ -115,18 +115,18 @@
                 null, null);
         if (c5 != null) {
             if (c5.getCount() > 0) {
-                if (DBG) Rlog.d(LOG_TAG, "Query Range returned the cursor " + c5);
+                if (DBG) Log.d(LOG_TAG, "Query Range returned the cursor " + c5);
                 c5.moveToFirst();
                 tmpMcc = c5.getInt(0);
-                if (DBG) Rlog.d(LOG_TAG, "SID found in mcc_sid_range. Return tmpMcc = " + tmpMcc);
+                if (DBG) Log.d(LOG_TAG, "SID found in mcc_sid_range. Return tmpMcc = " + tmpMcc);
                 c5.close();
                 return tmpMcc;
             }
             c5.close();
         }
-        if (DBG) Rlog.d(LOG_TAG, "SID NOT found in mcc_sid_range.");
+        if (DBG) Log.d(LOG_TAG, "SID NOT found in mcc_sid_range.");
 
-        if (DBG) Rlog.d(LOG_TAG, "Exit getMccByOtherFactors. Return tmpMcc =  " + tmpMcc);
+        if (DBG) Log.d(LOG_TAG, "Exit getMccByOtherFactors. Return tmpMcc =  " + tmpMcc);
         // If unknown MCC still could not be resolved,
         return tmpMcc;
     }
@@ -135,7 +135,7 @@
      *  Gets country information with given MCC.
     */
     public String getIddByMcc(int mcc) {
-        if (DBG) Rlog.d(LOG_TAG, "Enter getHbpcdInfoByMCC.");
+        if (DBG) Log.d(LOG_TAG, "Enter getHbpcdInfoByMCC.");
         String idd = "";
 
         Cursor c = null;
@@ -145,19 +145,19 @@
                 MccIdd.MCC + "=" + mcc, null, null);
         if (cur != null) {
             if (cur.getCount() > 0) {
-                if (DBG) Rlog.d(LOG_TAG, "Query Idd returned the cursor " + cur);
+                if (DBG) Log.d(LOG_TAG, "Query Idd returned the cursor " + cur);
                 // TODO: for those country having more than 1 IDDs, need more information
                 // to decide which IDD would be used. currently just use the first 1.
                 cur.moveToFirst();
                 idd = cur.getString(0);
-                if (DBG) Rlog.d(LOG_TAG, "IDD = " + idd);
+                if (DBG) Log.d(LOG_TAG, "IDD = " + idd);
 
             }
             cur.close();
         }
         if (c != null) c.close();
 
-        if (DBG) Rlog.d(LOG_TAG, "Exit getHbpcdInfoByMCC.");
+        if (DBG) Log.d(LOG_TAG, "Exit getHbpcdInfoByMCC.");
         return idd;
     }
 }
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index afb9b6f..9b82828 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 com.android.telephony.Rlog;
+import android.util.Log;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -565,7 +565,7 @@
             int mode = appOps.unsafeCheckOp(opStr, applicationData.mUid,
                     applicationData.mPackageName);
             if (mode != AppOpsManager.MODE_ALLOWED) {
-                Rlog.e(LOG_TAG, applicationData.mPackageName + " lost "
+                Log.e(LOG_TAG, applicationData.mPackageName + " lost "
                         + opStr + ": "
                         + (updateIfNeeded ? " (fixing)" : " (no permission to fix)"));
                 if (updateIfNeeded) {
@@ -643,7 +643,7 @@
                     int uid = packageManager.getPackageInfo(oldPackageName, 0).applicationInfo.uid;
                     setExclusiveAppops(oldPackageName, appOps, uid, AppOpsManager.MODE_DEFAULT);
                 } catch (NameNotFoundException e) {
-                    Rlog.w(LOG_TAG, "Old SMS package not found: " + oldPackageName);
+                    Log.w(LOG_TAG, "Old SMS package not found: " + oldPackageName);
                 }
             }
 
@@ -750,7 +750,7 @@
         // the package signature matches system signature.
         final int result = packageManager.checkSignatures(context.getPackageName(), packageName);
         if (result != PackageManager.SIGNATURE_MATCH) {
-            Rlog.e(LOG_TAG, packageName + " does not have system signature");
+            Log.e(LOG_TAG, packageName + " does not have system signature");
             return;
         }
         try {
@@ -758,13 +758,13 @@
             int mode = appOps.unsafeCheckOp(AppOpsManager.OPSTR_WRITE_SMS, info.applicationInfo.uid,
                     packageName);
             if (mode != AppOpsManager.MODE_ALLOWED) {
-                Rlog.w(LOG_TAG, packageName + " does not have OP_WRITE_SMS:  (fixing)");
+                Log.w(LOG_TAG, packageName + " does not have OP_WRITE_SMS:  (fixing)");
                 setExclusiveAppops(packageName, appOps, info.applicationInfo.uid,
                         AppOpsManager.MODE_ALLOWED);
             }
         } catch (NameNotFoundException e) {
             // No whitelisted system app on this device
-            Rlog.e(LOG_TAG, "Package not found: " + packageName);
+            Log.e(LOG_TAG, "Package not found: " + packageName);
         }
 
     }
diff --git a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
index 06c3728..cd365a1 100644
--- a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
+++ b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
@@ -24,13 +24,16 @@
 import android.os.SystemProperties;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
-import com.android.telephony.Rlog;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
 
 import com.android.internal.telephony.HbpcdLookup.MccIdd;
 import com.android.internal.telephony.HbpcdLookup.MccLookup;
 
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -143,7 +146,7 @@
 
         // First check whether the number is a NANP number.
         int nanpState = checkNANP(numberEntry, allIDDs);
-        if (DBG) Rlog.d(TAG, "NANP type: " + getNumberPlanType(nanpState));
+        if (DBG) Log.d(TAG, "NANP type: " + getNumberPlanType(nanpState));
 
         if ((nanpState == NP_NANP_LOCAL)
             || (nanpState == NP_NANP_AREA_LOCAL)
@@ -173,7 +176,7 @@
 
         int internationalState = checkInternationalNumberPlan(context, numberEntry, allIDDs,
                 NANP_IDD);
-        if (DBG) Rlog.d(TAG, "International type: " + getNumberPlanType(internationalState));
+        if (DBG) Log.d(TAG, "International type: " + getNumberPlanType(internationalState));
         String returnNumber = null;
 
         switch (internationalState) {
@@ -272,7 +275,7 @@
                 }
             }
         } catch (SQLException e) {
-            Rlog.e(TAG, "Can't access HbpcdLookup database", e);
+            Log.e(TAG, "Can't access HbpcdLookup database", e);
         } finally {
             if (cursor != null) {
                 cursor.close();
@@ -281,7 +284,7 @@
 
         IDDS_MAPS.put(mcc, allIDDs);
 
-        if (DBG) Rlog.d(TAG, "MCC = " + mcc + ", all IDDs = " + allIDDs);
+        if (DBG) Log.d(TAG, "MCC = " + mcc + ", all IDDs = " + allIDDs);
         return allIDDs;
     }
 
@@ -472,7 +475,7 @@
                 int tempCC = allCCs[i];
                 for (int j = 0; j < MAX_COUNTRY_CODES_LENGTH; j ++) {
                     if (tempCC == ccArray[j]) {
-                        if (DBG) Rlog.d(TAG, "Country code = " + tempCC);
+                        if (DBG) Log.d(TAG, "Country code = " + tempCC);
                         return tempCC;
                     }
                 }
@@ -509,7 +512,7 @@
                 }
             }
         } catch (SQLException e) {
-            Rlog.e(TAG, "Can't access HbpcdLookup database", e);
+            Log.e(TAG, "Can't access HbpcdLookup database", e);
         } finally {
             if (cursor != null) {
                 cursor.close();
@@ -561,10 +564,10 @@
      * Filter the destination number if using VZW sim card.
      */
     public static String filterDestAddr(Context context, int subId, String destAddr) {
-        if (DBG) Rlog.d(TAG, "enter filterDestAddr. destAddr=\"" + Rlog.pii(TAG, destAddr) + "\"" );
+        if (DBG) Log.d(TAG, "enter filterDestAddr. destAddr=\"" + pii(TAG, destAddr) + "\"" );
 
         if (destAddr == null || !PhoneNumberUtils.isGlobalPhoneNumber(destAddr)) {
-            Rlog.w(TAG, "destAddr" + Rlog.pii(TAG, destAddr) +
+            Log.w(TAG, "destAddr" + pii(TAG, destAddr) +
                     " is not a global phone number! Nothing changed.");
             return destAddr;
         }
@@ -585,9 +588,9 @@
         }
 
         if (DBG) {
-            Rlog.d(TAG, "destAddr is " + ((result != null)?"formatted.":"not formatted."));
-            Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + (result != null ? Rlog.pii(TAG,
-                    result) : Rlog.pii(TAG, destAddr)) + "\"");
+            Log.d(TAG, "destAddr is " + ((result != null)?"formatted.":"not formatted."));
+            Log.d(TAG, "leave filterDestAddr, new destAddr=\"" + (result != null ? pii(TAG,
+                    result) : pii(TAG, destAddr)) + "\"");
         }
         return result != null ? result : destAddr;
     }
@@ -608,7 +611,7 @@
                 networkType = CDMA_HOME_NETWORK;
             }
         } else {
-            if (DBG) Rlog.w(TAG, "warning! unknown mPhoneType value=" + phoneType);
+            if (DBG) Log.w(TAG, "warning! unknown mPhoneType value=" + phoneType);
         }
 
         return networkType;
@@ -650,4 +653,44 @@
         // by default this value is false
         return false;
     }
+
+    /**
+     * 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
+     */
+    private static String pii(String tag, Object pii) {
+        String val = String.valueOf(pii);
+        if (pii == null || TextUtils.isEmpty(val) || Log.isLoggable(tag, Log.VERBOSE)) {
+            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 (android.os.Build.IS_USER) {
+            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/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 4109ca6..f6ce0dc 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -28,7 +28,6 @@
 import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
-import com.android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -521,7 +520,7 @@
             return;
         }
 
-        if (DBG) Rlog.d(LOG_TAG, "No modify permission, check carrier privilege next.");
+        if (DBG) Log.d(LOG_TAG, "No modify permission, check carrier privilege next.");
         enforceCallingOrSelfCarrierPrivilege(context, subId, message);
     }
 
@@ -539,7 +538,7 @@
         }
 
         if (DBG) {
-            Rlog.d(LOG_TAG, "No READ_PHONE_STATE permission, check carrier privilege next.");
+            Log.d(LOG_TAG, "No READ_PHONE_STATE permission, check carrier privilege next.");
         }
 
         enforceCallingOrSelfCarrierPrivilege(context, subId, message);
@@ -559,7 +558,7 @@
         }
 
         if (DBG) {
-            Rlog.d(LOG_TAG, "No READ_PRIVILEDED_PHONE_STATE permission, "
+            Log.d(LOG_TAG, "No READ_PRIVILEDED_PHONE_STATE permission, "
                     + "check carrier privilege next.");
         }
 
@@ -584,7 +583,7 @@
             Context context, int subId, int uid, String message) {
         if (getCarrierPrivilegeStatus(context, subId, uid)
                 != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
-            if (DBG) Rlog.e(LOG_TAG, "No Carrier Privilege.");
+            if (DBG) Log.e(LOG_TAG, "No Carrier Privilege.");
             throw new SecurityException(message);
         }
     }
diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java
index e01deb2..1e1cdba 100644
--- a/telephony/java/android/telephony/CallQuality.java
+++ b/telephony/java/android/telephony/CallQuality.java
@@ -80,6 +80,9 @@
     private int mMaxRelativeJitter;
     private int mAverageRoundTripTime;
     private int mCodecType;
+    private boolean mRtpInactivityDetected;
+    private boolean mRxSilenceDetected;
+    private boolean mTxSilenceDetected;
 
     /** @hide **/
     public CallQuality(Parcel in) {
@@ -94,6 +97,9 @@
         mMaxRelativeJitter = in.readInt();
         mAverageRoundTripTime = in.readInt();
         mCodecType = in.readInt();
+        mRtpInactivityDetected = in.readBoolean();
+        mRxSilenceDetected = in.readBoolean();
+        mTxSilenceDetected = in.readBoolean();
     }
 
     /** @hide **/
@@ -109,7 +115,7 @@
      * @param numRtpPacketsReceived RTP packets received from network
      * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
      * transmitted
-     * @param numRtpPacketsNotReceived RTP packets which were lost in network and never recieved
+     * @param numRtpPacketsNotReceived RTP packets which were lost in network and never received
      * @param averageRelativeJitter average relative jitter in milliseconds
      * @param maxRelativeJitter maximum relative jitter in milliseconds
      * @param averageRoundTripTime average round trip delay in milliseconds
@@ -127,6 +133,48 @@
             int maxRelativeJitter,
             int averageRoundTripTime,
             int codecType) {
+        this(downlinkCallQualityLevel, uplinkCallQualityLevel, callDuration,
+            numRtpPacketsTransmitted, numRtpPacketsReceived, numRtpPacketsTransmittedLost,
+            numRtpPacketsNotReceived, averageRelativeJitter, maxRelativeJitter,
+            averageRoundTripTime, codecType, false, false, false);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param callQualityLevel the call quality level (see #CallQualityLevel)
+     * @param callDuration the call duration in milliseconds
+     * @param numRtpPacketsTransmitted RTP packets sent to network
+     * @param numRtpPacketsReceived RTP packets received from network
+     * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
+     * transmitted
+     * @param numRtpPacketsNotReceived RTP packets which were lost in network and never received
+     * @param averageRelativeJitter average relative jitter in milliseconds
+     * @param maxRelativeJitter maximum relative jitter in milliseconds
+     * @param averageRoundTripTime average round trip delay in milliseconds
+     * @param codecType the codec type
+     * @param rtpInactivityDetected True if no incoming RTP is received for a continuous duration of
+     * 4 seconds
+     * @param rxSilenceDetected True if only silence RTP packets are received for 20 seconds
+     * immediately after call is connected
+     * @param txSilenceDetected True if only silence RTP packets are sent for 20 seconds immediately
+     * after call is connected
+     */
+    public CallQuality(
+            @CallQualityLevel int downlinkCallQualityLevel,
+            @CallQualityLevel int uplinkCallQualityLevel,
+            int callDuration,
+            int numRtpPacketsTransmitted,
+            int numRtpPacketsReceived,
+            int numRtpPacketsTransmittedLost,
+            int numRtpPacketsNotReceived,
+            int averageRelativeJitter,
+            int maxRelativeJitter,
+            int averageRoundTripTime,
+            int codecType,
+            boolean rtpInactivityDetected,
+            boolean rxSilenceDetected,
+            boolean txSilenceDetected) {
         this.mDownlinkCallQualityLevel = downlinkCallQualityLevel;
         this.mUplinkCallQualityLevel = uplinkCallQualityLevel;
         this.mCallDuration = callDuration;
@@ -138,6 +186,9 @@
         this.mMaxRelativeJitter = maxRelativeJitter;
         this.mAverageRoundTripTime = averageRoundTripTime;
         this.mCodecType = codecType;
+        this.mRtpInactivityDetected = rtpInactivityDetected;
+        this.mRxSilenceDetected = rxSilenceDetected;
+        this.mTxSilenceDetected = txSilenceDetected;
     }
 
     // getters
@@ -226,6 +277,29 @@
     }
 
     /**
+     * Returns true if no rtp packets are received continuously for the last 4 seconds
+     */
+    public boolean isRtpInactivityDetected() {
+        return mRtpInactivityDetected;
+    }
+
+    /**
+     * Returns true if only silence rtp packets are received for a duration of 20 seconds starting
+     * at call setup
+     */
+    public boolean isIncomingSilenceDetected() {
+        return mRxSilenceDetected;
+    }
+
+    /**
+      * Returns true if only silence rtp packets are sent for a duration of 20 seconds starting at
+      * call setup
+      */
+    public boolean isOutgoingSilenceDetected() {
+        return mTxSilenceDetected;
+    }
+
+    /**
      * Returns the codec type. This value corresponds to the AUDIO_QUALITY_* constants in
      * {@link ImsStreamMediaProfile}.
      *
@@ -270,6 +344,9 @@
                 + " maxRelativeJitter=" + mMaxRelativeJitter
                 + " averageRoundTripTime=" + mAverageRoundTripTime
                 + " codecType=" + mCodecType
+                + " rtpInactivityDetected=" + mRtpInactivityDetected
+                + " txSilenceDetected=" + mRxSilenceDetected
+                + " rxSilenceDetected=" + mTxSilenceDetected
                 + "}";
     }
 
@@ -286,7 +363,10 @@
                 mAverageRelativeJitter,
                 mMaxRelativeJitter,
                 mAverageRoundTripTime,
-                mCodecType);
+                mCodecType,
+                mRtpInactivityDetected,
+                mRxSilenceDetected,
+                mTxSilenceDetected);
     }
 
     @Override
@@ -311,7 +391,10 @@
                 && mAverageRelativeJitter == s.mAverageRelativeJitter
                 && mMaxRelativeJitter == s.mMaxRelativeJitter
                 && mAverageRoundTripTime == s.mAverageRoundTripTime
-                && mCodecType == s.mCodecType);
+                && mCodecType == s.mCodecType
+                && mRtpInactivityDetected == s.mRtpInactivityDetected
+                && mRxSilenceDetected == s.mRxSilenceDetected
+                && mTxSilenceDetected == s.mTxSilenceDetected);
     }
 
     /**
@@ -336,6 +419,9 @@
         dest.writeInt(mMaxRelativeJitter);
         dest.writeInt(mAverageRoundTripTime);
         dest.writeInt(mCodecType);
+        dest.writeBoolean(mRtpInactivityDetected);
+        dest.writeBoolean(mRxSilenceDetected);
+        dest.writeBoolean(mTxSilenceDetected);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<CallQuality> CREATOR = new Parcelable.Creator() {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6a62237..8617833 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -672,6 +672,12 @@
             "carrier_promote_wfc_on_call_fail_bool";
 
     /**
+     * Flag specifying whether provisioning is required for RCS.
+     */
+    public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL =
+            "carrier_rcs_provisioning_required_bool";
+
+    /**
      * Flag specifying whether provisioning is required for VoLTE, Video Telephony, and WiFi
      * Calling.
      */
@@ -848,9 +854,12 @@
             "carrier_force_disable_etws_cmas_test_bool";
 
     /**
-     * The default flag specifying whether "Turn on Notifications" option will be always shown in
-     * Settings->More->Emergency broadcasts menu regardless developer options is turned on or not.
+     * The default flag specifying whether "Allow alerts" option will be always shown in
+     * emergency alerts settings regardless developer options is turned on or not.
+     *
+     * @deprecated The allow alerts option is always shown now. No longer need a config for that.
      */
+    @Deprecated
     public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL =
             "always_show_emergency_alert_onoff_bool";
 
@@ -1627,8 +1636,8 @@
      * Defines carrier-specific actions which act upon
      * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
      * and configured signal args:
-     * {@link com.android.internal.telephony.TelephonyIntents#EXTRA_APN_TYPE_KEY apnType},
-     * {@link com.android.internal.telephony.TelephonyIntents#EXTRA_ERROR_CODE_KEY errorCode}
+     * {@link TelephonyManager#EXTRA_APN_TYPE apnType},
+     * {@link TelephonyManager#EXTRA_ERROR_CODE errorCode}
      * used for customization of the default carrier app
      * Format:
      * {
@@ -3497,6 +3506,7 @@
         sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT, 2);
         sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT, 2);
         sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL, false);
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 39af34c..c706d28 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -17,6 +17,8 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -35,7 +37,26 @@
 
     private Context mContext;
 
-    /** @hide */
+    /**
+     * <p>Broadcast Action: Indicates that an IMS operation was rejected by the network due to it
+     * not being authorized on the network.
+     * May include the {@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX} extra to also specify
+     * which subscription the operation was rejected for.
+     * <p class="note">
+     * Carrier applications may listen to this broadcast to be notified of possible IMS provisioning
+     * issues.
+     */
+    // Moved from TelephonyIntents, need to keep backwards compatibility with OEM apps that have
+    // this value hard-coded in BroadcastReceiver.
+    @SuppressLint("ActionValue")
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION =
+            "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
+
+    /**
+     * Use {@link Context#getSystemService(String)} to get an instance of this class.
+     * @hide
+     */
     public ImsManager(@NonNull Context context) {
         mContext = context;
     }
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 31434c1..5e2e554 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -20,6 +20,9 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.LinkProperties;
 import android.os.Build;
@@ -31,8 +34,6 @@
 import android.telephony.Annotation.NetworkType;
 import android.telephony.data.ApnSetting;
 
-import dalvik.system.VMRuntime;
-
 import java.util.Objects;
 
 
@@ -134,6 +135,13 @@
     }
 
     /**
+     * To check the SDK version for {@link PreciseDataConnectionState#getDataConnectionState}.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    private static final long GET_DATA_CONNECTION_STATE_CODE_CHANGE = 147600208L;
+
+    /**
      * Returns the state of data connection that supported the apn types returned by
      * {@link #getDataConnectionApnTypeBitMask()}
      *
@@ -144,7 +152,7 @@
     @SystemApi
     public @DataState int getDataConnectionState() {
         if (mState == TelephonyManager.DATA_DISCONNECTING
-                && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) {
+                && !Compatibility.isChangeEnabled(GET_DATA_CONNECTION_STATE_CODE_CHANGE)) {
             return TelephonyManager.DATA_CONNECTED;
         }
 
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index eb328a7..1dbc8b1 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -281,6 +281,42 @@
      */
     public static final int SMS_MESSAGE_PERIOD_NOT_SPECIFIED = -1;
 
+    /** @hide */
+    @IntDef(prefix = { "PREMIUM_SMS_CONSENT" }, value = {
+        SmsManager.PREMIUM_SMS_CONSENT_UNKNOWN,
+        SmsManager.PREMIUM_SMS_CONSENT_ASK_USER,
+        SmsManager.PREMIUM_SMS_CONSENT_NEVER_ALLOW,
+        SmsManager.PREMIUM_SMS_CONSENT_ALWAYS_ALLOW
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PremiumSmsConsent {}
+
+    /** Premium SMS Consent for the package is unknown. This indicates that the user
+     *  has not set a permission for this package, because this package has never tried
+     *  to send a premium SMS.
+     * @hide
+     */
+    @SystemApi
+    public static final int PREMIUM_SMS_CONSENT_UNKNOWN = 0;
+
+    /** Default premium SMS Consent (ask user for each premium SMS sent).
+     * @hide
+     */
+    @SystemApi
+    public static final int PREMIUM_SMS_CONSENT_ASK_USER = 1;
+
+    /** Premium SMS Consent when the owner has denied the app from sending premium SMS.
+     * @hide
+     */
+    @SystemApi
+    public static final int PREMIUM_SMS_CONSENT_NEVER_ALLOW = 2;
+
+    /** Premium SMS Consent when the owner has allowed the app to send premium SMS.
+     * @hide
+     */
+    @SystemApi
+    public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3;
+
     // result of asking the user for a subscription to perform an operation.
     private interface SubscriptionResolverResult {
         void onSuccess(int subId);
@@ -744,11 +780,7 @@
                     "Invalid pdu format. format must be either 3gpp or 3gpp2");
         }
         try {
-            ISms iSms = ISms.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSmsServiceRegisterer()
-                            .get());
+            ISms iSms = TelephonyManager.getSmsService();
             if (iSms != null) {
                 iSms.injectSmsPduForSubscriber(
                         getSubscriptionId(), pdu, format, receivedIntent);
@@ -1571,7 +1603,7 @@
      * the service does not exist.
      */
     private static ISms getISmsServiceOrThrow() {
-        ISms iSms = getISmsService();
+        ISms iSms = TelephonyManager.getSmsService();
         if (iSms == null) {
             throw new UnsupportedOperationException("Sms is not supported");
         }
@@ -1579,11 +1611,7 @@
     }
 
     private static ISms getISmsService() {
-        return ISms.Stub.asInterface(
-                TelephonyFrameworkInitializer
-                        .getTelephonyServiceManager()
-                        .getSmsServiceRegisterer()
-                        .get());
+        return TelephonyManager.getSmsService();
     }
 
     /**
@@ -2017,11 +2045,7 @@
     public boolean isSMSPromptEnabled() {
         ISms iSms = null;
         try {
-            iSms = ISms.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSmsServiceRegisterer()
-                            .get());
+            iSms = TelephonyManager.getSmsService();
             return iSms.isSMSPromptEnabled();
         } catch (RemoteException ex) {
             return false;
@@ -2448,14 +2472,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);
+        }
     }
 
     /**
@@ -2479,6 +2507,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) {
@@ -2488,8 +2517,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
@@ -2531,9 +2563,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) {
@@ -2542,7 +2574,7 @@
         } catch (RemoteException ex) {
             // ignore it
         }
-        return null;
+        return new Bundle();
     }
 
     /**
@@ -2861,4 +2893,53 @@
         }
         return false;
     }
+
+    /**
+     * Gets the premium SMS permission for the specified package. If the package has never
+     * been seen before, the default {@link SmsManager#PREMIUM_SMS_PERMISSION_ASK_USER}
+     * will be returned.
+     * @param packageName the name of the package to query permission
+     * @return one of {@link SmsManager#PREMIUM_SMS_CONSENT_UNKNOWN},
+     *  {@link SmsManager#PREMIUM_SMS_CONSENT_ASK_USER},
+     *  {@link SmsManager#PREMIUM_SMS_CONSENT_NEVER_ALLOW}, or
+     *  {@link SmsManager#PREMIUM_SMS_CONSENT_ALWAYS_ALLOW}
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @PremiumSmsConsent int getPremiumSmsConsent(@NonNull String packageName) {
+        int permission = 0;
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                permission = iSms.getPremiumSmsPermission(packageName);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "getPremiumSmsPermission() RemoteException", e);
+        }
+        return permission;
+    }
+
+    /**
+     * Sets the premium SMS permission for the specified package and save the value asynchronously
+     * to persistent storage.
+     * @param packageName the name of the package to set permission
+     * @param permission one of {@link SmsManager#PREMIUM_SMS_CONSENT_ASK_USER},
+     *  {@link SmsManager#PREMIUM_SMS_CONSENT_NEVER_ALLOW}, or
+     *  {@link SmsManager#PREMIUM_SMS_CONSENT_ALWAYS_ALLOW}
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setPremiumSmsConsent(
+            @NonNull String packageName, @PremiumSmsConsent int permission) {
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                iSms.setPremiumSmsPermission(packageName, permission);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "setPremiumSmsPermission() RemoteException", e);
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index c217b8b..40a7619 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -20,8 +20,13 @@
 
 import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
 
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.StringDef;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.os.Binder;
@@ -31,8 +36,10 @@
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
 import com.android.internal.telephony.Sms7BitEncodingTranslator;
 import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
 import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
+import com.android.internal.telephony.cdma.sms.UserData;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -56,6 +63,16 @@
         UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
     }
 
+    /** @hide */
+    @IntDef(prefix = { "ENCODING_" }, value = {
+            ENCODING_UNKNOWN,
+            ENCODING_7BIT,
+            ENCODING_8BIT,
+            ENCODING_16BIT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EncodingSize {}
+
     /** User data text encoding code unit size */
     public static final int ENCODING_UNKNOWN = 0;
     public static final int ENCODING_7BIT = 1;
@@ -633,6 +650,83 @@
     }
 
     /**
+     * Get an SMS-SUBMIT PDU's encoded message.
+     * This is used by Bluetooth MAP profile to handle long non UTF-8 SMS messages.
+     *
+     * @param isTypeGsm true when message's type is GSM, false when type is CDMA
+     * @param destinationAddress the address of the destination for the message
+     * @param message message content
+     * @param encoding User data text encoding code unit size
+     * @param languageTable GSM national language table to use, specified by 3GPP
+     *                      23.040 9.2.3.24.16
+     * @param languageShiftTable GSM national language shift table to use, specified by 3GPP
+     *                           23.040 9.2.3.24.15
+     * @param refNumber parameter to create SmsHeader
+     * @param seqNumber parameter to create SmsHeader
+     * @param msgCount parameter to create SmsHeader
+     * @return a byte[] containing the encoded message
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    @SystemApi
+    @NonNull
+    public static byte[] getSubmitPduEncodedMessage(boolean isTypeGsm,
+            @NonNull String destinationAddress,
+            @NonNull String message,
+            @EncodingSize int encoding, int languageTable,
+            int languageShiftTable, int refNumber,
+            int seqNumber, int msgCount) {
+        byte[] data;
+        SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
+        concatRef.refNumber = refNumber;
+        concatRef.seqNumber = seqNumber;  // 1-based sequence
+        concatRef.msgCount = msgCount;
+        // We currently set this to true since our messaging app will never
+        // send more than 255 parts (it converts the message to MMS well before that).
+        // However, we should support 3rd party messaging apps that might need 16-bit
+        // references
+        // Note:  It's not sufficient to just flip this bit to true; it will have
+        // ripple effects (several calculations assume 8-bit ref).
+        concatRef.isEightBits = true;
+        SmsHeader smsHeader = new SmsHeader();
+        smsHeader.concatRef = concatRef;
+
+        /* Depending on the type, call either GSM or CDMA getSubmitPdu(). The encoding
+         * will be determined(again) by getSubmitPdu().
+         * All packets need to be encoded using the same encoding, as the bMessage
+         * only have one filed to describe the encoding for all messages in a concatenated
+         * SMS... */
+        if (encoding == ENCODING_7BIT) {
+            smsHeader.languageTable = languageTable;
+            smsHeader.languageShiftTable = languageShiftTable;
+        }
+
+        if (isTypeGsm) {
+            data = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(null,
+                    destinationAddress, message, false,
+                    SmsHeader.toByteArray(smsHeader), encoding, languageTable,
+                    languageShiftTable).encodedMessage;
+        } else { // SMS_TYPE_CDMA
+            UserData uData = new UserData();
+            uData.payloadStr = message;
+            uData.userDataHeader = smsHeader;
+            if (encoding == ENCODING_7BIT) {
+                uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
+            } else { // assume UTF-16
+                uData.msgEncoding = UserData.ENCODING_UNICODE_16;
+            }
+            uData.msgEncodingSet = true;
+            data = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
+                    destinationAddress, uData, false).encodedMessage;
+        }
+        if (data == null) {
+            return new byte[0];
+        }
+        return data;
+    }
+
+    /**
      * Returns the address of the SMS service center that relayed this message
      * or null if there is none.
      */
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 4510fed..cb4462f 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1146,11 +1146,7 @@
         SubscriptionInfo subInfo = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1184,11 +1180,7 @@
         SubscriptionInfo result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1222,11 +1214,7 @@
         SubscriptionInfo result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
                         mContext.getOpPackageName(), mContext.getFeatureId());
@@ -1250,11 +1238,7 @@
         List<SubscriptionInfo> result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getAllSubInfoList(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1335,11 +1319,7 @@
         List<SubscriptionInfo> activeList = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1390,11 +1370,7 @@
         List<SubscriptionInfo> result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1433,11 +1409,7 @@
         List<SubscriptionInfo> result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getAccessibleSubscriptionInfoList(mContext.getOpPackageName());
             }
@@ -1466,11 +1438,7 @@
     public void requestEmbeddedSubscriptionInfoListRefresh() {
         int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc();
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
             }
@@ -1499,11 +1467,7 @@
     @SystemApi
     public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
             }
@@ -1524,11 +1488,7 @@
         int result = 0;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getAllSubInfoCount(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1557,11 +1517,7 @@
         int result = 0;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1582,11 +1538,7 @@
         int result = 0;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getActiveSubInfoCountMax();
             }
@@ -1643,11 +1595,7 @@
         }
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub == null) {
                 Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- ISub service is null");
                 return;
@@ -1681,11 +1629,7 @@
         }
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub == null) {
                 Log.e(LOG_TAG, "[removeSubscriptionInfoRecord]- ISub service is null");
                 return;
@@ -1788,11 +1732,7 @@
         int result = INVALID_SIM_SLOT_INDEX;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getSlotIndex(subscriptionId);
             }
@@ -1826,11 +1766,7 @@
         int[] subId = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subId = iSub.getSubId(slotIndex);
             }
@@ -1854,11 +1790,7 @@
         int result = INVALID_PHONE_INDEX;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getPhoneId(subId);
             }
@@ -1892,11 +1824,7 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subId = iSub.getDefaultSubId();
             }
@@ -1919,11 +1847,7 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subId = iSub.getDefaultVoiceSubId();
             }
@@ -1953,11 +1877,7 @@
     public void setDefaultVoiceSubscriptionId(int subscriptionId) {
         if (VDBG) logd("setDefaultVoiceSubId sub id = " + subscriptionId);
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.setDefaultVoiceSubId(subscriptionId);
             }
@@ -2005,11 +1925,7 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subId = iSub.getDefaultSmsSubId();
             }
@@ -2035,11 +1951,7 @@
     public void setDefaultSmsSubId(int subscriptionId) {
         if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId);
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.setDefaultSmsSubId(subscriptionId);
             }
@@ -2077,11 +1989,7 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subId = iSub.getDefaultDataSubId();
             }
@@ -2107,11 +2015,7 @@
     public void setDefaultDataSubId(int subscriptionId) {
         if (VDBG) logd("setDataSubscription sub id = " + subscriptionId);
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.setDefaultDataSubId(subscriptionId);
             }
@@ -2142,11 +2046,7 @@
     /** @hide */
     public void clearSubscriptionInfo() {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.clearSubInfo();
             }
@@ -2282,11 +2182,7 @@
      */
     public @NonNull int[] getActiveSubscriptionIdList(boolean visibleOnly) {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 int[] subId = iSub.getActiveSubIdList(visibleOnly);
                 if (subId != null) return subId;
@@ -2337,11 +2233,7 @@
         int simState = TelephonyManager.SIM_STATE_UNKNOWN;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 simState = iSub.getSimStateForSlotIndex(slotIndex);
             }
@@ -2360,11 +2252,7 @@
      */
     public static void setSubscriptionProperty(int subId, String propKey, String propValue) {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.setSubscriptionProperty(subId, propKey, propValue);
             }
@@ -2384,11 +2272,7 @@
             Context context) {
         String resultValue = null;
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 resultValue = iSub.getSubscriptionProperty(subId, propKey,
                         context.getOpPackageName(), context.getFeatureId());
@@ -2530,11 +2414,7 @@
     @UnsupportedAppUsage
     public boolean isActiveSubId(int subId) {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 return iSub.isActiveSubId(subId, mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -2837,11 +2717,7 @@
             @TelephonyManager.SetOpportunisticSubscriptionResult Consumer<Integer> callback) {
         if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId);
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub == null) return;
 
             ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() {
@@ -2884,11 +2760,7 @@
     public int getPreferredDataSubscriptionId() {
         int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 preferredSubId = iSub.getPreferredDataSubscriptionId();
             }
@@ -2919,11 +2791,7 @@
         List<SubscriptionInfo> subInfoList = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, contextFeature);
             }
@@ -3024,11 +2892,7 @@
         ParcelUuid groupUuid = null;
         int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 groupUuid = iSub.createSubscriptionGroup(subIdArray, pkgForDebug);
             } else {
@@ -3078,11 +2942,7 @@
         int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.addSubscriptionsIntoGroup(subIdArray, groupUuid, pkgForDebug);
             } else {
@@ -3134,11 +2994,7 @@
         int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.removeSubscriptionsFromGroup(subIdArray, groupUuid, pkgForDebug);
             } else {
@@ -3183,11 +3039,7 @@
 
         List<SubscriptionInfo> result = null;
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, contextFeature);
             } else {
@@ -3300,11 +3152,7 @@
             logd("setSubscriptionActivated subId= " + subscriptionId + " enable " + enable);
         }
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 return iSub.setSubscriptionEnabled(enable, subscriptionId);
             }
@@ -3393,11 +3241,7 @@
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isSubscriptionEnabled(int subscriptionId) {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 return iSub.isSubscriptionEnabled(subscriptionId);
             }
@@ -3420,11 +3264,7 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subId = iSub.getEnabledSubscriptionId(slotIndex);
             }
@@ -3450,11 +3290,7 @@
         int result = 0;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = helper.callMethod(iSub);
             }
@@ -3477,11 +3313,7 @@
      */
     public static int getActiveDataSubscriptionId() {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 return iSub.getActiveDataSubscriptionId();
             }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 405b3a5..d15a531 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -57,6 +57,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
@@ -94,12 +95,15 @@
 import android.util.Pair;
 
 import com.android.ims.internal.IImsServiceFeatureCallback;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.INumberVerificationCallback;
 import com.android.internal.telephony.IOns;
 import com.android.internal.telephony.IPhoneSubInfo;
 import com.android.internal.telephony.ISetOpportunisticDataCallback;
+import com.android.internal.telephony.ISms;
+import com.android.internal.telephony.ISub;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.ITelephonyRegistry;
 import com.android.internal.telephony.IUpdateAvailableNetworksCallback;
@@ -109,8 +113,6 @@
 import com.android.internal.telephony.SmsApplication;
 import com.android.telephony.Rlog;
 
-import dalvik.system.VMRuntime;
-
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.lang.annotation.Retention;
@@ -301,6 +303,21 @@
     private SubscriptionManager mSubscriptionManager;
     private TelephonyScanManager mTelephonyScanManager;
 
+    /** Cached service handles, cleared by resetServiceHandles() at death */
+    private static final Object sCacheLock = new Object();
+
+    /** @hide */
+    private static boolean sServiceHandleCacheEnabled = true;
+
+    @GuardedBy("sCacheLock")
+    private static IPhoneSubInfo sIPhoneSubInfo;
+    @GuardedBy("sCacheLock")
+    private static ISub sISub;
+    @GuardedBy("sCacheLock")
+    private static ISms sISms;
+    @GuardedBy("sCacheLock")
+    private static final DeathRecipient sServiceDeath = new DeathRecipient();
+
     /** Enum indicating multisim variants
      *  DSDS - Dual SIM Dual Standby
      *  DSDA - Dual SIM Dual Active
@@ -1444,24 +1461,6 @@
             "android.telephony.extra.SIM_COMBINATION_NAMES";
 
     /**
-     * Broadcast Action: The time was set by the carrier (typically by the NITZ string).
-     * This is a sticky broadcast.
-     * The intent will have the following extra values:</p>
-     * <ul>
-     *   <li><em>time</em> - The time as a long in UTC milliseconds.</li>
-     * </ul>
-     *
-     * <p class="note">
-     * Requires the READ_PHONE_STATE permission.
-     *
-     * <p class="note">This is a protected intent that can only be sent by the system.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final String ACTION_NETWORK_SET_TIME = "android.telephony.action.NETWORK_SET_TIME";
-
-    /**
      * <p>Broadcast Action: The emergency callback mode is changed.
      * <ul>
      *   <li><em>phoneinECMState</em> - A boolean value,true=phone in ECM, false=ECM off</li>
@@ -1482,6 +1481,193 @@
             = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
 
     /**
+     * <p>Broadcast Action: when data connections get redirected with validation failure.
+     * intended for sim/account status checks and only sent to the specified carrier app
+     * The intent will have the following extra values:</p>
+     * <ul>
+     *   <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd>
+     *   <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd>
+     *   <li>{@link #EXTRA_REDIRECTION_URL}</li><dd>redirection url string</dd>
+     *   <li>subId</li><dd>Sub Id which associated the data connection failure.</dd>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system.</p>
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String ACTION_CARRIER_SIGNAL_REDIRECTED =
+            "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED";
+
+    /**
+     * <p>Broadcast Action: when data connections setup fails.
+     * intended for sim/account status checks and only sent to the specified carrier app
+     * The intent will have the following extra values:</p>
+     * <ul>
+     *   <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd>
+     *   <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd>
+     *   <li>{@link #EXTRA_ERROR_CODE}</li><dd>A integer with dataFailCause.</dd>
+     *   <li>subId</li><dd>Sub Id which associated the data connection failure.</dd>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system. </p>
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED =
+            "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED";
+
+    /**
+     * <p>Broadcast Action: when pco value is available.
+     * intended for sim/account status checks and only sent to the specified carrier app
+     * The intent will have the following extra values:</p>
+     * <ul>
+     *   <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd>
+     *   <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd>
+     *   <li>{@link #EXTRA_APN_PROTOCOL}</li><dd>A string with the protocol of the apn connection
+     *      (IP,IPV6, IPV4V6)</dd>
+     *   <li>{@link #EXTRA_APN_PROTOCOL_INT}</li><dd>A integer with the protocol of the apn
+     *      connection (IP,IPV6, IPV4V6)</dd>
+     *   <li>{@link #EXTRA_PCO_ID}</li><dd>An integer indicating the pco id for the data.</dd>
+     *   <li>{@link #EXTRA_PCO_VALUE}</li><dd>A byte array of pco data read from modem.</dd>
+     *   <li>subId</li><dd>Sub Id which associated the data connection.</dd>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system. </p>
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE =
+            "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE";
+
+    /**
+     * <p>Broadcast Action: when system default network available/unavailable with
+     * carrier-disabled mobile data. Intended for carrier apps to set/reset carrier actions when
+     * other network becomes system default network, Wi-Fi for example.
+     * The intent will have the following extra values:</p>
+     * <ul>
+     *   <li>{@link #EXTRA_DEFAULT_NETWORK_AVAILABLE}</li>
+     *   <dd>A boolean indicates default network available.</dd>
+     *   <li>subId</li><dd>Sub Id which associated the default data.</dd>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system. </p>
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE =
+            "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
+
+    /**
+     * <p>Broadcast Action: when framework reset all carrier actions on sim load or absent.
+     * intended for carrier apps clean up (clear UI e.g.) and only sent to the specified carrier app
+     * The intent will have the following extra values:</p>
+     * <ul>
+     *   <li>subId</li><dd>Sub Id which associated the data connection failure.</dd>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system.</p>
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String ACTION_CARRIER_SIGNAL_RESET =
+            "com.android.internal.telephony.CARRIER_SIGNAL_RESET";
+
+    // CARRIER_SIGNAL_ACTION extra keys
+    /**
+     *  An string extra of redirected url upon {@link #ACTION_CARRIER_SIGNAL_REDIRECTED}.
+     *  @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String EXTRA_REDIRECTION_URL = "redirectionUrl";
+
+    /**
+     *  An integer extra of error code upon {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}.
+     *  Check {@link DataFailCause} for all possible values.
+     *  @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String EXTRA_ERROR_CODE = "errorCode";
+
+    /**
+     *  An string extra of corresponding apn type upon
+     *  {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED},
+     *  {@link #ACTION_CARRIER_SIGNAL_REDIRECTED} and
+     *  {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts.
+     *  @deprecated This is kept for backward compatibility reason. Use {@link #EXTRA_APN_TYPE_INT}
+     *  instead.
+     *
+     *  @hide
+     */
+    @SystemApi
+    @Deprecated
+    @SuppressLint("ActionValue")
+    public static final String EXTRA_APN_TYPE = "apnType";
+
+    /**
+     *  An string integer of corresponding apn type upon
+     *  {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED},
+     *  {@link #ACTION_CARRIER_SIGNAL_REDIRECTED} and
+     *  {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts.
+     *  Check {@link ApnSetting} TYPE_* for its values.
+     *  @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String EXTRA_APN_TYPE_INT = "apnTypeInt";
+
+    /**
+     *  An string extra with the protocol of the apn connection (IP,IPV6, IPV4V6) upon
+     *  {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts.
+     *  @deprecated This is kept for backward compatibility reason.
+     *  Use {@link #EXTRA_APN_PROTOCOL_INT} instead.
+     *
+     *  @hide
+     */
+    @SystemApi
+    @Deprecated
+    @SuppressLint("ActionValue")
+    public static final String EXTRA_APN_PROTOCOL = "apnProto";
+
+    /**
+     *  An integer extra with the protocol of the apn connection (IP,IPV6, IPV4V6) upon
+     *  {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts.
+     *  Check {@link ApnSetting} PROTOCOL_* for its values.
+     *  @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt";
+
+    /**
+     *  An integer extra indicating the pco id for the data upon
+     *  {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts.
+     *  @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String EXTRA_PCO_ID = "pcoId";
+
+    /**
+     *  An extra of byte array of pco data read from modem upon
+     *  {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts.
+     *  @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String EXTRA_PCO_VALUE = "pcoValue";
+
+    /**
+     *  An boolean extra indicating default network available upon
+     *  {@link #ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE} broadcasts.
+     *  @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable";
+
+    /**
      * <p>Broadcast Action: The emergency call state is changed.
      * <ul>
      *   <li><em>phoneInEmergencyCall</em> - A boolean value, true if phone in emergency call,
@@ -1680,7 +1866,7 @@
     public String getDeviceId(int slotIndex) {
         // FIXME this assumes phoneId == slotIndex
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getDeviceIdForPhone(slotIndex, mContext.getOpPackageName(),
@@ -1934,7 +2120,7 @@
 
     private String getNaiBySubscriberId(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName(),
@@ -3623,7 +3809,7 @@
     @UnsupportedAppUsage
     public String getSimSerialNumber(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName(),
@@ -3897,7 +4083,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getSubscriberId(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName(),
@@ -3933,7 +4119,7 @@
     @Nullable
     public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(@KeyType int keyType) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null) {
                 Rlog.e(TAG,"IMSI error: Subscriber Info is null");
                 return null;
@@ -3976,7 +4162,7 @@
     @SystemApi
     public void resetCarrierKeysForImsiEncryption() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null) {
                 Rlog.e(TAG, "IMSI error: Subscriber Info is null");
                 if (!isSystemProcess()) {
@@ -4041,7 +4227,7 @@
      */
     public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null) return;
             info.setCarrierInfoForImsiEncryption(mSubId, mContext.getOpPackageName(),
                     imsiEncryptionInfo);
@@ -4065,7 +4251,7 @@
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getGroupIdLevel1() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName(),
@@ -4089,7 +4275,7 @@
     @UnsupportedAppUsage
     public String getGroupIdLevel1(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName(),
@@ -4152,7 +4338,7 @@
             return number;
         }
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName(),
@@ -4243,7 +4429,7 @@
             return alphaTag;
         }
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName(),
@@ -4331,7 +4517,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getMsisdn(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getMsisdnForSubscriber(subId, getOpPackageName(), getFeatureId());
@@ -4365,7 +4551,7 @@
     @UnsupportedAppUsage
     public String getVoiceMailNumber(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName(),
@@ -4964,7 +5150,7 @@
     @UnsupportedAppUsage
     public String getVoiceMailAlphaTag(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName(),
@@ -5012,7 +5198,7 @@
     @UnsupportedAppUsage
     public String getIsimImpi() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             //get the Isim Impi based on subId
@@ -5039,7 +5225,7 @@
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getIsimDomain() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             //get the Isim Domain based on subId
@@ -5058,10 +5244,12 @@
      *      not present or not loaded
      * @hide
      */
-    @UnsupportedAppUsage
+    @Nullable
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String[] getIsimImpu() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             //get the Isim Impu based on subId
@@ -5074,19 +5262,6 @@
         }
     }
 
-   /**
-    * @hide
-    */
-    @UnsupportedAppUsage
-    private IPhoneSubInfo getSubscriberInfo() {
-        // get it each time because that process crashes a lot
-        return IPhoneSubInfo.Stub.asInterface(
-                TelephonyFrameworkInitializer
-                        .getTelephonyServiceManager()
-                        .getPhoneSubServiceRegisterer()
-                        .get());
-    }
-
     /**
      * Device call state: No activity.
      */
@@ -5240,6 +5415,13 @@
     public static final int DATA_DISCONNECTING = 4;
 
     /**
+     * To check the SDK version for {@link TelephonyManager#getDataState}.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    private static final long GET_DATA_STATE_CODE_CHANGE = 147600208L;
+
+    /**
      * Returns a constant indicating the current data connection state
      * (cellular).
      *
@@ -5257,7 +5439,7 @@
             int state = telephony.getDataStateForSubId(
                     getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
             if (state == TelephonyManager.DATA_DISCONNECTING
-                    && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) {
+                    && !Compatibility.isChangeEnabled(GET_DATA_STATE_CODE_CHANGE)) {
                 return TelephonyManager.DATA_CONNECTED;
             }
 
@@ -5319,6 +5501,13 @@
     //
 
     /**
+     * To check the SDK version for {@link TelephonyManager#listen}.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    private static final long LISTEN_CODE_CHANGE = 147600208L;
+
+    /**
      * Registers a listener object to receive notification of changes
      * in specified telephony states.
      * <p>
@@ -5357,7 +5546,7 @@
                 // subId from PhoneStateListener is deprecated Q on forward, use the subId from
                 // TelephonyManager instance. keep using subId from PhoneStateListener for pre-Q.
                 int subId = mSubId;
-                if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
+                if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {
                     // since mSubId in PhoneStateListener is deprecated from Q on forward, this is
                     // the only place to set mSubId and its for "informational" only.
                     //  TODO: remove this once we completely get rid of mSubId in PhoneStateListener
@@ -6823,7 +7012,7 @@
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getIsimIst() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             //get the Isim Ist based on subId
@@ -6845,7 +7034,7 @@
     @UnsupportedAppUsage
     public String[] getIsimPcscf() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             //get the Isim Pcscf based on subId
@@ -6926,7 +7115,7 @@
     @UnsupportedAppUsage
     public String getIccAuthentication(int subId, int appType, int authType, String data) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getIccSimChallengeResponse(subId, appType, authType, data);
@@ -12202,4 +12391,150 @@
         }
         return false;
     }
+
+    private static class DeathRecipient implements IBinder.DeathRecipient {
+        @Override
+        public void binderDied() {
+            resetServiceCache();
+        }
+    }
+
+   /**
+    * Reset everything in the service cache; if one handle died then they are
+    * all probably broken.
+    * @hide
+    */
+    private static void resetServiceCache() {
+        synchronized (sCacheLock) {
+            if (sISub != null) {
+                sISub.asBinder().unlinkToDeath(sServiceDeath, 0);
+                sISub = null;
+            }
+            if (sISms != null) {
+                sISms.asBinder().unlinkToDeath(sServiceDeath, 0);
+                sISms = null;
+            }
+            if (sIPhoneSubInfo != null) {
+                sIPhoneSubInfo.asBinder().unlinkToDeath(sServiceDeath, 0);
+                sIPhoneSubInfo = null;
+            }
+        }
+    }
+
+   /**
+    * @hide
+    */
+    static IPhoneSubInfo getSubscriberInfoService() {
+        if (!sServiceHandleCacheEnabled) {
+            return IPhoneSubInfo.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getPhoneSubServiceRegisterer()
+                        .get());
+        }
+
+        if (sIPhoneSubInfo == null) {
+            IPhoneSubInfo temp = IPhoneSubInfo.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getPhoneSubServiceRegisterer()
+                        .get());
+            synchronized (sCacheLock) {
+                if (sIPhoneSubInfo == null && temp != null) {
+                    try {
+                        sIPhoneSubInfo = temp;
+                        sIPhoneSubInfo.asBinder().linkToDeath(sServiceDeath, 0);
+                    } catch (Exception e) {
+                        // something has gone horribly wrong
+                        sIPhoneSubInfo = null;
+                    }
+                }
+            }
+        }
+        return sIPhoneSubInfo;
+    }
+
+   /**
+    * @hide
+    */
+    static ISub getSubscriptionService() {
+        if (!sServiceHandleCacheEnabled) {
+            return ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
+        }
+
+        if (sISub == null) {
+            ISub temp = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
+            synchronized (sCacheLock) {
+                if (sISub == null && temp != null) {
+                    try {
+                        sISub = temp;
+                        sISub.asBinder().linkToDeath(sServiceDeath, 0);
+                    } catch (Exception e) {
+                        // something has gone horribly wrong
+                        sISub = null;
+                    }
+                }
+            }
+        }
+        return sISub;
+    }
+
+    /**
+    * @hide
+    */
+    static ISms getSmsService() {
+        if (!sServiceHandleCacheEnabled) {
+            return ISms.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSmsServiceRegisterer()
+                            .get());
+        }
+
+        if (sISms == null) {
+            ISms temp = ISms.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSmsServiceRegisterer()
+                            .get());
+            synchronized (sCacheLock) {
+                if (sISms == null && temp != null) {
+                    try {
+                        sISms = temp;
+                        sISms.asBinder().linkToDeath(sServiceDeath, 0);
+                    } catch (Exception e) {
+                        // something has gone horribly wrong
+                        sISms = null;
+                    }
+                }
+            }
+        }
+        return sISms;
+    }
+
+    /**
+     * Disables service handle caching for tests that utilize mock services.
+     * @hide
+     */
+    @VisibleForTesting
+    public static void disableServiceHandleCaching() {
+        sServiceHandleCacheEnabled = false;
+    }
+
+    /**
+     * Reenables service handle caching.
+     * @hide
+     */
+    @VisibleForTesting
+    public static void enableServiceHandleCaching() {
+        sServiceHandleCacheEnabled = true;
+    }
 }
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index c962714..d483291 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -27,6 +27,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.telephony.AccessNetworkConstants;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRcsController;
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index f052180..6005f77 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -34,6 +34,7 @@
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.aidl.IImsConfigCallback;
 import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.RcsFeature;
 import android.telephony.ims.stub.ImsConfigImplBase;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 
@@ -84,6 +85,11 @@
             "STRING_QUERY_RESULT_ERROR_NOT_READY";
 
     /**
+     * There is no existing configuration for the queried provisioning key.
+     */
+    public static final int PROVISIONING_RESULT_UNKNOWN = -1;
+
+    /**
      * The integer result of provisioning for the queried key is disabled.
      */
     public static final int PROVISIONING_VALUE_DISABLED = 0;
@@ -94,6 +100,151 @@
     public static final int PROVISIONING_VALUE_ENABLED = 1;
 
 
+    // Inheriting values from ImsConfig for backwards compatibility.
+    /**
+     * An integer key representing the SIP T1 timer value in milliseconds for the associated
+     * subscription.
+     * <p>
+     * The SIP T1 timer is an estimate of the round-trip time and will retransmit
+     * INVITE transactions that are longer than T1 milliseconds over unreliable transports, doubling
+     * the time before retransmission every time there is no response. See RFC3261, section 17.1.1.1
+     * for more details.
+     * <p>
+     * The value is an integer.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_T1_TIMER_VALUE_MS = 7;
+
+    /**
+     * An integer key representing the voice over LTE (VoLTE) provisioning status for the
+     * associated subscription. Determines whether the user can register for voice services over
+     * LTE.
+     * <p>
+     * Use {@link #PROVISIONING_VALUE_ENABLED} to enable VoLTE provisioning and
+     * {@link #PROVISIONING_VALUE_DISABLED} to disable VoLTE provisioning.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_VOLTE_PROVISIONING_STATUS = 10;
+
+    /**
+     * An integer key representing the video telephony (VT) provisioning status for the
+     * associated subscription. Determines whether the user can register for video services over
+     * LTE.
+     * <p>
+     * Use {@link #PROVISIONING_VALUE_ENABLED} to enable VT provisioning and
+     * {@link #PROVISIONING_VALUE_DISABLED} to disable VT provisioning.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_VT_PROVISIONING_STATUS = 11;
+
+    /**
+     * An integer key associated with the carrier configured SIP PUBLISH timer, which dictates the
+     * expiration time in seconds for published online availability in RCS presence.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15;
+
+    /**
+     * An integer key associated with the carrier configured expiration time in seconds for
+     * RCS presence published offline availability in RCS presence.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16;
+
+    /**
+     * An integer key associated with whether or not capability discovery is provisioned for this
+     * subscription. Any capability requests will be ignored by the RCS service.
+     * <p>
+     * The value is an integer, either {@link #PROVISIONING_VALUE_DISABLED} if capability
+     * discovery is disabled or {@link #PROVISIONING_VALUE_ENABLED} if capability discovery is
+     * enabled.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17;
+
+    /**
+     * An integer key associated with the period of time the capability information of each contact
+     * is cached on the device.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18;
+
+    /**
+     * An integer key associated with the period of time in seconds that the availability
+     * information of a contact is cached on the device.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19;
+
+    /**
+     * An integer key associated with the carrier configured interval in seconds expected between
+     * successive capability polling attempts.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20;
+
+    /**
+     * An integer key representing the minimum time allowed between two consecutive presence publish
+     * messages from the device.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21;
+
+    /**
+     * An integer key associated with the maximum number of MDNs contained in one SIP Request
+     * Contained List (RCS) used to retrieve the RCS capabilities of the contacts book.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22;
+
+    /**
+     * An integer associated with the expiration timer used duriing the SIP subscription of a
+     * Request Contained List (RCL), which is used to retrieve the RCS capabilities of the contact
+     * book.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23;
+
+    /**
+     * An integer key representing the RCS enhanced address book (EAB) provisioning status for the
+     * associated subscription. Determines whether or not SIP OPTIONS or presence will be used to
+     * retrieve RCS capabilities for the user's contacts.
+     * <p>
+     * Use {@link #PROVISIONING_VALUE_ENABLED} to enable EAB provisioning and
+     * {@link #PROVISIONING_VALUE_DISABLED} to disable EAB provisioning.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_EAB_PROVISIONING_STATUS = 25;
+
     /**
      * Override the user-defined WiFi Roaming enabled setting for this subscription, defined in
      * {@link SubscriptionManager#WFC_ROAMING_ENABLED_CONTENT_URI}, for the purposes of provisioning
@@ -269,7 +420,7 @@
      *
      * @param key An integer that represents the provisioning key, which is defined by the OEM.
      * @return an integer value for the provided key, or
-     * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN} if the key doesn't exist.
+     * {@link #PROVISIONING_RESULT_UNKNOWN} if the key doesn't exist.
      * @throws IllegalArgumentException if the key provided was invalid.
      */
     @WorkerThread
@@ -396,6 +547,54 @@
     }
 
     /**
+     * Get the provisioning status for the IMS RCS capability specified.
+     *
+     * If provisioning is not required for the queried
+     * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method will always return
+     * {@code true}.
+     *
+     * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+     * @return true if the device is provisioned for the capability or does not require
+     * provisioning, false if the capability does require provisioning and has not been
+     * provisioned yet.
+     */
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean getRcsProvisioningStatusForCapability(
+            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+        try {
+            return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the provisioning status for the IMS RCS capability using the specified subscription.
+     *
+     * Provisioning may or may not be required, depending on the carrier configuration. If
+     * provisioning is not required for the carrier associated with this subscription or the device
+     * does not support the capability/technology combination specified, this operation will be a
+     * no-op.
+     *
+     * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+     * @param isProvisioned true if the device is provisioned for the RCS capability specified,
+     *                      false otherwise.
+     */
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setRcsProvisioningStatusForCapability(
+            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            boolean isProvisioned) {
+        try {
+            getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
+                    isProvisioned);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
      * Notify the framework that an RCS autoconfiguration XML file has been received for
      * provisioning.
      * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed.
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index 492170b..893a311 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -35,6 +36,7 @@
  * Contains the User Capability Exchange capabilities corresponding to a contact's URI.
  * @hide
  */
+@SystemApi
 public final class RcsContactUceCapability implements Parcelable {
 
     /** Supports 1-to-1 chat */
@@ -135,7 +137,7 @@
          * @param type The capability to map to a service URI that is different from the contact's
          *         URI.
          */
-        public Builder add(@CapabilityFlag int type, @NonNull Uri serviceUri) {
+        public @NonNull Builder add(@CapabilityFlag int type, @NonNull Uri serviceUri) {
             mCapabilities.mCapabilities |= type;
             // Put each of these capabilities into the map separately.
             for (int shift = 0; shift < Integer.SIZE; shift++) {
@@ -157,7 +159,7 @@
          * Add a UCE capability flag that this contact supports.
          * @param type the capability that the contact supports.
          */
-        public Builder add(@CapabilityFlag int type) {
+        public @NonNull Builder add(@CapabilityFlag int type) {
             mCapabilities.mCapabilities |= type;
             return this;
         }
@@ -167,7 +169,7 @@
          * @param extension A string containing a carrier specific service tag that is an extension
          *         of the {@link CapabilityFlag}s that are defined here.
          */
-        public Builder add(@NonNull String extension) {
+        public @NonNull Builder add(@NonNull String extension) {
             mCapabilities.mExtensionTags.add(extension);
             return this;
         }
@@ -175,7 +177,7 @@
         /**
          * @return the constructed instance.
          */
-        public RcsContactUceCapability build() {
+        public @NonNull RcsContactUceCapability build() {
             return mCapabilities;
         }
     }
@@ -205,7 +207,7 @@
         }
     }
 
-    public static final Creator<RcsContactUceCapability> CREATOR =
+    public static final @NonNull Creator<RcsContactUceCapability> CREATOR =
             new Creator<RcsContactUceCapability>() {
         @Override
         public RcsContactUceCapability createFromParcel(Parcel in) {
@@ -219,7 +221,7 @@
     };
 
     @Override
-    public void writeToParcel(Parcel out, int flags) {
+    public void writeToParcel(@NonNull Parcel out, int flags) {
         out.writeParcelable(mContactUri, 0);
         out.writeInt(mCapabilities);
         out.writeStringList(mExtensionTags);
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index d18e93c..60cf216 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
+import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsConfigCallback;
 import android.util.Log;
@@ -228,7 +229,8 @@
      * The configuration requested resulted in an unknown result. This may happen if the
      * IMS configurations are unavailable.
      */
-    public static final int CONFIG_RESULT_UNKNOWN = -1;
+    public static final int CONFIG_RESULT_UNKNOWN = ProvisioningManager.PROVISIONING_RESULT_UNKNOWN;
+
     /**
      * Setting the configuration value completed.
      */
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index 9116a3b..0d86e2b 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -178,7 +178,7 @@
          * SIP T1 timer value in milliseconds. See RFC 3261 for define.
          * Value is in Integer format.
          */
-        public static final int SIP_T1_TIMER = 7;
+        public static final int SIP_T1_TIMER = ProvisioningManager.KEY_T1_TIMER_VALUE_MS;
 
         /**
          * SIP T2 timer value in milliseconds.  See RFC 3261 for define.
@@ -196,13 +196,15 @@
          * VoLTE status for VLT/s status of Enabled (1), or Disabled (0).
          * Value is in Integer format.
          */
-        public static final int VLT_SETTING_ENABLED = 10;
+        public static final int VLT_SETTING_ENABLED =
+                ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
 
         /**
          * VoLTE status for LVC/s status of Enabled (1), or Disabled (0).
          * Value is in Integer format.
          */
-        public static final int LVC_SETTING_ENABLED = 11;
+        public static final int LVC_SETTING_ENABLED =
+                ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
         /**
          * Domain Name for the device to populate the request URI for REGISTRATION.
          * Value is in String format.
@@ -222,48 +224,56 @@
          * Requested expiration for Published Online availability.
          * Value is in Integer format.
          */
-        public static final int PUBLISH_TIMER = 15;
+        public static final int PUBLISH_TIMER = ProvisioningManager.KEY_RCS_PUBLISH_TIMER_SEC;
         /**
          * Requested expiration for Published Offline availability.
          * Value is in Integer format.
          */
-        public static final int PUBLISH_TIMER_EXTENDED = 16;
+        public static final int PUBLISH_TIMER_EXTENDED =
+                ProvisioningManager.KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC;
         /**
          *
          * Value is in Integer format.
          */
-        public static final int CAPABILITY_DISCOVERY_ENABLED = 17;
+        public static final int CAPABILITY_DISCOVERY_ENABLED =
+                ProvisioningManager.KEY_RCS_CAPABILITY_DISCOVERY_ENABLED;
         /**
          * Period of time the capability information of the  contact is cached on handset.
          * Value is in Integer format.
          */
-        public static final int CAPABILITIES_CACHE_EXPIRATION = 18;
+        public static final int CAPABILITIES_CACHE_EXPIRATION =
+                ProvisioningManager.KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC;
         /**
          * Peiod of time the availability information of a contact is cached on device.
          * Value is in Integer format.
          */
-        public static final int AVAILABILITY_CACHE_EXPIRATION = 19;
+        public static final int AVAILABILITY_CACHE_EXPIRATION =
+                ProvisioningManager.KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC;
         /**
          * Interval between successive capabilities polling.
          * Value is in Integer format.
          */
-        public static final int CAPABILITIES_POLL_INTERVAL = 20;
+        public static final int CAPABILITIES_POLL_INTERVAL =
+                ProvisioningManager.KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC;
         /**
          * Minimum time between two published messages from the device.
          * Value is in Integer format.
          */
-        public static final int SOURCE_THROTTLE_PUBLISH = 21;
+        public static final int SOURCE_THROTTLE_PUBLISH =
+                ProvisioningManager.KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS;
         /**
          * The Maximum number of MDNs contained in one Request Contained List.
          * Value is in Integer format.
          */
-        public static final int MAX_NUMENTRIES_IN_RCL = 22;
+        public static final int MAX_NUMENTRIES_IN_RCL =
+                ProvisioningManager.KEY_RCS_MAX_NUM_ENTRIES_IN_RCL;
         /**
          * Expiration timer for subscription of a Request Contained List, used in capability
          * polling.
          * Value is in Integer format.
          */
-        public static final int CAPAB_POLL_LIST_SUB_EXP = 23;
+        public static final int CAPAB_POLL_LIST_SUB_EXP =
+                ProvisioningManager.KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC;
         /**
          * Applies compression to LIST Subscription.
          * Value is in Integer format. Enable (1), Disable(0).
@@ -273,7 +283,8 @@
          * VOLTE Status for EAB/s status of Enabled (1), or Disabled (0).
          * Value is in Integer format.
          */
-        public static final int EAB_SETTING_ENABLED = 25;
+        public static final int EAB_SETTING_ENABLED =
+                ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
         /**
          * Wi-Fi calling roaming status.
          * Value is in Integer format. ON (1), OFF(0).
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index b846a10..28f3974 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1977,6 +1977,17 @@
      */
     boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech);
 
+    /**
+     * Get the provisioning status for the IMS Rcs capability specified.
+     */
+    boolean getRcsProvisioningStatusForCapability(int subId, int capability);
+
+    /**
+     * Set the provisioning status for the IMS Rcs capability using the specified subscription.
+     */
+    void setRcsProvisioningStatusForCapability(int subId, int capability,
+            boolean isProvisioned);
+
     /** Is the capability and tech flagged as provisioned in the cache */
     boolean isMmTelCapabilityProvisionedInCache(int subId, int capability, int tech);
 
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 54c07cf..a15f73c 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -19,6 +19,7 @@
 import android.content.Intent;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsManager;
 
 /**
  * The intents that the telephony services broadcast.
@@ -198,9 +199,11 @@
      * <p class="note">
      * This is for the OEM applications to understand about possible provisioning issues.
      * Used in OMA-DM applications.
+     * @deprecated Use {@link ImsManager#ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION} instead.
      */
-    public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION
-            = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
+    @Deprecated
+    public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION =
+            ImsManager.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION;
 
     /**
      * Broadcast Action: A "secret code" has been entered in the dialer. Secret codes are
@@ -325,85 +328,6 @@
             "android.intent.action.ACTION_SET_RADIO_CAPABILITY_FAILED";
 
     /**
-     * <p>Broadcast Action: when data connections get redirected with validation failure.
-     * intended for sim/account status checks and only sent to the specified carrier app
-     * The intent will have the following extra values:</p>
-     * <ul>
-     *   <li>apnType</li><dd>A string with the apn type.</dd>
-     *   <li>redirectionUrl</li><dd>redirection url string</dd>
-     *   <li>subId</li><dd>Sub Id which associated the data connection failure.</dd>
-     * </ul>
-     * <p class="note">This is a protected intent that can only be sent by the system.</p>
-     */
-    public static final String ACTION_CARRIER_SIGNAL_REDIRECTED =
-            "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED";
-    /**
-     * <p>Broadcast Action: when data connections setup fails.
-     * intended for sim/account status checks and only sent to the specified carrier app
-     * The intent will have the following extra values:</p>
-     * <ul>
-     *   <li>apnType</li><dd>A string with the apn type.</dd>
-     *   <li>errorCode</li><dd>A integer with dataFailCause.</dd>
-     *   <li>subId</li><dd>Sub Id which associated the data connection failure.</dd>
-     * </ul>
-     * <p class="note">This is a protected intent that can only be sent by the system. </p>
-     */
-    public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED =
-            "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED";
-
-    /**
-     * <p>Broadcast Action: when pco value is available.
-     * intended for sim/account status checks and only sent to the specified carrier app
-     * The intent will have the following extra values:</p>
-     * <ul>
-     *   <li>apnType</li><dd>A string with the apn type.</dd>
-     *   <li>apnProto</li><dd>A string with the protocol of the apn connection (IP,IPV6,
-     *                        IPV4V6)</dd>
-     *   <li>pcoId</li><dd>An integer indicating the pco id for the data.</dd>
-     *   <li>pcoValue</li><dd>A byte array of pco data read from modem.</dd>
-     *   <li>subId</li><dd>Sub Id which associated the data connection.</dd>
-     * </ul>
-     * <p class="note">This is a protected intent that can only be sent by the system. </p>
-     */
-    public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE =
-            "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE";
-
-    /**
-     * <p>Broadcast Action: when system default network available/unavailable with
-     * carrier-disabled mobile data. Intended for carrier apps to set/reset carrier actions when
-     * other network becomes system default network, Wi-Fi for example.
-     * The intent will have the following extra values:</p>
-     * <ul>
-     *   <li>defaultNetworkAvailable</li><dd>A boolean indicates default network available.</dd>
-     *   <li>subId</li><dd>Sub Id which associated the default data.</dd>
-     * </ul>
-     * <p class="note">This is a protected intent that can only be sent by the system. </p>
-     */
-    public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE =
-            "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
-
-    /**
-     * <p>Broadcast Action: when framework reset all carrier actions on sim load or absent.
-     * intended for carrier apps clean up (clear UI e.g.) and only sent to the specified carrier app
-     * The intent will have the following extra values:</p>
-     * <ul>
-     *   <li>subId</li><dd>Sub Id which associated the data connection failure.</dd>
-     * </ul>
-     * <p class="note">This is a protected intent that can only be sent by the system.</p>
-     */
-    public static final String ACTION_CARRIER_SIGNAL_RESET =
-            "com.android.internal.telephony.CARRIER_SIGNAL_RESET";
-
-    // CARRIER_SIGNAL_ACTION extra keys
-    public static final String EXTRA_REDIRECTION_URL_KEY = "redirectionUrl";
-    public static final String EXTRA_ERROR_CODE_KEY = "errorCode";
-    public static final String EXTRA_APN_TYPE_KEY = "apnType";
-    public static final String EXTRA_APN_PROTO_KEY = "apnProto";
-    public static final String EXTRA_PCO_ID_KEY = "pcoId";
-    public static final String EXTRA_PCO_VALUE_KEY = "pcoValue";
-    public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY = "defaultNetworkAvailable";
-
-    /**
      * Broadcast action to trigger CI OMA-DM Session.
      */
     public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
diff --git a/tests/net/common/java/android/net/CaptivePortalTest.java b/tests/net/common/java/android/net/CaptivePortalTest.java
index eed7159f..ca4ba63 100644
--- a/tests/net/common/java/android/net/CaptivePortalTest.java
+++ b/tests/net/common/java/android/net/CaptivePortalTest.java
@@ -44,6 +44,11 @@
         }
 
         @Override
+        public void appRequest(final int request) throws RemoteException {
+            mCode = request;
+        }
+
+        @Override
         public void logEvent(int eventId, String packageName) throws RemoteException {
             mCode = eventId;
             mPackageName = packageName;
@@ -80,6 +85,12 @@
     }
 
     @Test
+    public void testReevaluateNetwork() {
+        final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.reevaluateNetwork());
+        assertEquals(result.mCode, CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED);
+    }
+
+    @Test
     public void testLogEvent() {
         final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.logEvent(
                 MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY,
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 1e8d83c..0ab5c97 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -35,10 +35,10 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
-import android.net.NetworkFactory;
 import android.net.NetworkInfo;
-import android.net.NetworkMisc;
+import android.net.NetworkProvider;
 import android.net.NetworkSpecifier;
 import android.net.SocketKeepalive;
 import android.net.UidRange;
@@ -114,7 +114,7 @@
         public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp) {
             super(wrapper.mHandlerThread.getLooper(), wrapper.mContext, wrapper.mLogTag,
                     wrapper.mNetworkInfo, wrapper.mNetworkCapabilities, lp, wrapper.mScore,
-                    new NetworkMisc(), NetworkFactory.SerialNumber.NONE);
+                    new NetworkAgentConfig(), NetworkProvider.ID_NONE);
             mWrapper = wrapper;
         }
 
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 535298f..9e915ae 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -35,10 +35,10 @@
 import android.net.IDnsResolver;
 import android.net.INetd;
 import android.net.Network;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
-import android.net.NetworkFactory;
 import android.net.NetworkInfo;
-import android.net.NetworkMisc;
+import android.net.NetworkProvider;
 import android.net.NetworkScore;
 import android.os.INetworkManagementService;
 import android.text.format.DateUtils;
@@ -75,7 +75,7 @@
     @Mock INetd mNetd;
     @Mock INetworkManagementService mNMS;
     @Mock Context mCtx;
-    @Mock NetworkMisc mMisc;
+    @Mock NetworkAgentConfig mAgentConfig;
     @Mock NetworkNotificationManager mNotifier;
     @Mock Resources mResources;
 
@@ -358,8 +358,8 @@
         NetworkScore ns = new NetworkScore();
         ns.putIntExtension(NetworkScore.LEGACY_SCORE, 50);
         NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
-                caps, ns, mCtx, null, mMisc, mConnService, mNetd, mDnsResolver, mNMS,
-                NetworkFactory.SerialNumber.NONE);
+                caps, ns, mCtx, null, mAgentConfig, mConnService, mNetd, mDnsResolver, mNMS,
+                NetworkProvider.ID_NONE);
         nai.everValidated = true;
         return nai;
     }
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index b709af1..cf70f5d 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -33,8 +33,8 @@
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkInfo;
-import android.net.NetworkMisc;
 import android.os.Handler;
 import android.os.INetworkManagementService;
 import android.os.test.TestLooper;
@@ -63,7 +63,7 @@
     static final int NETID = 42;
 
     @Mock ConnectivityService mConnectivity;
-    @Mock NetworkMisc mMisc;
+    @Mock NetworkAgentConfig mAgentConfig;
     @Mock IDnsResolver mDnsResolver;
     @Mock INetd mNetd;
     @Mock INetworkManagementService mNms;
@@ -93,7 +93,7 @@
         mNai.networkInfo = new NetworkInfo(null);
         mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
         when(mNai.connService()).thenReturn(mConnectivity);
-        when(mNai.netMisc()).thenReturn(mMisc);
+        when(mNai.netAgentConfig()).thenReturn(mAgentConfig);
         when(mNai.handler()).thenReturn(mHandler);
 
         when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig);
@@ -104,7 +104,7 @@
         String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
                 nai.networkInfo.getDetailedState(),
-                mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
+                mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
                 nai.linkProperties.getLinkAddresses());
         assertEquals(msg, expected, Nat464Xlat.requiresClat(nai));
     }
@@ -113,7 +113,7 @@
         String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
                 nai.networkInfo.getDetailedState(),
-                mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
+                mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
                 nai.linkProperties.getLinkAddresses());
         assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai));
     }
@@ -151,11 +151,11 @@
                 assertRequiresClat(true, mNai);
                 assertShouldStartClat(true, mNai);
 
-                mMisc.skip464xlat = true;
+                mAgentConfig.skip464xlat = true;
                 assertRequiresClat(false, mNai);
                 assertShouldStartClat(false, mNai);
 
-                mMisc.skip464xlat = false;
+                mAgentConfig.skip464xlat = false;
                 assertRequiresClat(true, mNai);
                 assertShouldStartClat(true, mNai);
 
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 6de068e..a9e0b9a 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -80,6 +80,7 @@
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -102,6 +103,7 @@
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
 import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.TestableNetworkStatsProvider;
 
 import libcore.io.IoUtils;
 
@@ -1001,6 +1003,88 @@
         mService.unregisterUsageRequest(unknownRequest);
     }
 
+    @Test
+    public void testStatsProviderUpdateStats() throws Exception {
+        // Pretend that network comes online.
+        expectDefaultSettings();
+        final NetworkState[] states = new NetworkState[]{buildWifiState(true /* isMetered */)};
+        expectNetworkStatsSummary(buildEmptyStats());
+        expectNetworkStatsUidDetail(buildEmptyStats());
+
+        // Register custom provider and retrieve callback.
+        final TestableNetworkStatsProvider provider = new TestableNetworkStatsProvider();
+        final INetworkStatsProviderCallback cb =
+                mService.registerNetworkStatsProvider("TEST", provider);
+        assertNotNull(cb);
+
+        mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+
+        // Verifies that one requestStatsUpdate will be called during iface update.
+        provider.expectStatsUpdate(0 /* unused */);
+
+        // Create some initial traffic and report to the service.
+        incrementCurrentTime(HOUR_IN_MILLIS);
+        final NetworkStats expectedStats = new NetworkStats(0L, 1)
+                .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+                        TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
+                        128L, 2L, 128L, 2L, 1L))
+                .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+                        0xF00D, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
+                        64L, 1L, 64L, 1L, 1L));
+        cb.onStatsUpdated(0 /* unused */, expectedStats, expectedStats);
+
+        // Make another empty mutable stats object. This is necessary since the new NetworkStats
+        // object will be used to compare with the old one in NetworkStatsRecoder, two of them
+        // cannot be the same object.
+        expectNetworkStatsUidDetail(buildEmptyStats());
+
+        forcePollAndWaitForIdle();
+
+        // Verifies that one requestStatsUpdate and setAlert will be called during polling.
+        provider.expectStatsUpdate(0 /* unused */);
+        provider.expectSetAlert(MB_IN_BYTES);
+
+        // Verifies that service recorded history, does not verify uid tag part.
+        assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1);
+
+        // Verifies that onStatsUpdated updates the stats accordingly.
+        final NetworkStats stats = mSession.getSummaryForAllUid(
+                sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
+        assertEquals(2, stats.size());
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1L);
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1L);
+
+        // Verifies that unregister the callback will remove the provider from service.
+        cb.unregister();
+        forcePollAndWaitForIdle();
+        provider.assertNoCallback();
+    }
+
+    @Test
+    public void testStatsProviderSetAlert() throws Exception {
+        // Pretend that network comes online.
+        expectDefaultSettings();
+        NetworkState[] states = new NetworkState[]{buildWifiState(true /* isMetered */)};
+        mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+
+        // Register custom provider and retrieve callback.
+        final TestableNetworkStatsProvider provider = new TestableNetworkStatsProvider();
+        final INetworkStatsProviderCallback cb =
+                mService.registerNetworkStatsProvider("TEST", provider);
+        assertNotNull(cb);
+
+        // Simulates alert quota of the provider has been reached.
+        cb.onAlertReached();
+        HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT);
+
+        // Verifies that polling is triggered by alert reached.
+        provider.expectStatsUpdate(0 /* unused */);
+        // Verifies that global alert will be re-armed.
+        provider.expectSetAlert(MB_IN_BYTES);
+    }
+
     private static File getBaseDir(File statsDir) {
         File baseDir = new File(statsDir, "netstats");
         baseDir.mkdirs();
@@ -1102,6 +1186,7 @@
     private void expectSettings(long persistBytes, long bucketDuration, long deleteAge)
             throws Exception {
         when(mSettings.getPollInterval()).thenReturn(HOUR_IN_MILLIS);
+        when(mSettings.getPollDelay()).thenReturn(0L);
         when(mSettings.getSampleEnabled()).thenReturn(true);
 
         final Config config = new Config(bucketDuration, deleteAge, deleteAge);
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 3623b11..469128b 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -800,7 +800,12 @@
       }
 
       // This is a normal reference.
-      return util::make_unique<Reference>(data, ref_type);
+      auto reference = util::make_unique<Reference>(data, ref_type);
+      if (res_value.dataType == android::Res_value::TYPE_DYNAMIC_REFERENCE ||
+          res_value.dataType == android::Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
+        reference->is_dynamic = true;
+      }
+      return reference;
     } break;
   }
 
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index c016cb4..b08bf9a 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -109,6 +109,20 @@
   EXPECT_TRUE(private_ref);
 }
 
+TEST(ResourceUtilsTest, ParseBinaryDynamicReference) {
+  android::Res_value value = {};
+  value.data = util::HostToDevice32(0x01);
+  value.dataType = android::Res_value::TYPE_DYNAMIC_REFERENCE;
+  std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(ResourceType::kId,
+                                                                  android::ConfigDescription(),
+                                                                  android::ResStringPool(), value,
+                                                                  nullptr);
+
+  Reference* ref = ValueCast<Reference>(item.get());
+  EXPECT_TRUE(ref->is_dynamic);
+  EXPECT_EQ(ref->id.value().id, 0x01);
+}
+
 TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) {
   bool create = false;
   bool private_ref = false;
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 7498e13..8a2f5af 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -269,6 +269,11 @@
   }
 }
 
+// Message holding a boolean, so it can be optionally encoded.
+message Boolean {
+  bool value = 1;
+}
+
 // A value that is a reference to another resource. This reference can be by name or resource ID.
 message Reference {
   enum Type {
@@ -289,6 +294,9 @@
 
   // Whether this reference is referencing a private resource (@*package:type/entry).
   bool private = 4;
+
+  // Whether this reference is dynamic.
+  Boolean is_dynamic = 5;
 }
 
 // A value that represents an ID. This is just a placeholder, as ID values are used to occupy a
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 4555caa..5b6935b 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -895,7 +895,7 @@
           // android:versionCode from the framework AndroidManifest.xml.
           ExtractCompileSdkVersions(asset_source->GetAssetManager());
         }
-      } else if (asset_source->IsPackageDynamic(entry.first)) {
+      } else if (asset_source->IsPackageDynamic(entry.first, entry.second)) {
         final_table_.included_packages_[entry.first] = entry.second;
       }
     }
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index efbf636..4cd6e93 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -634,6 +634,7 @@
                                        std::string* out_error) {
   out_ref->reference_type = DeserializeReferenceTypeFromPb(pb_ref.type());
   out_ref->private_reference = pb_ref.private_();
+  out_ref->is_dynamic = pb_ref.is_dynamic().value();
 
   if (pb_ref.id() != 0) {
     out_ref->id = ResourceId(pb_ref.id());
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index e4b3fce..d9f6c19 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -418,6 +418,9 @@
 
   pb_ref->set_private_(ref.private_reference);
   pb_ref->set_type(SerializeReferenceTypeToPb(ref.reference_type));
+  if (ref.is_dynamic) {
+    pb_ref->mutable_is_dynamic()->set_value(ref.is_dynamic);
+  }
 }
 
 template <typename T>
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index e7f2330..61a8335 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -608,4 +608,41 @@
   ASSERT_FALSE(search_result.value().entry->overlayable_item);
 }
 
+TEST(ProtoSerializeTest, SerializeAndDeserializeDynamicReference) {
+  Reference ref(ResourceId(0x00010001));
+  ref.is_dynamic = true;
+
+  pb::Item pb_item;
+  SerializeItemToPb(ref, &pb_item);
+
+  ASSERT_TRUE(pb_item.has_ref());
+  EXPECT_EQ(pb_item.ref().id(), ref.id.value().id);
+  EXPECT_TRUE(pb_item.ref().is_dynamic().value());
+
+  std::unique_ptr<Item> item = DeserializeItemFromPb(pb_item, android::ResStringPool(),
+                                                     android::ConfigDescription(), nullptr,
+                                                     nullptr, nullptr);
+  Reference* actual_ref = ValueCast<Reference>(item.get());
+  EXPECT_EQ(actual_ref->id.value().id, ref.id.value().id);
+  EXPECT_TRUE(actual_ref->is_dynamic);
+}
+
+TEST(ProtoSerializeTest, SerializeAndDeserializeNonDynamicReference) {
+  Reference ref(ResourceId(0x00010001));
+
+  pb::Item pb_item;
+  SerializeItemToPb(ref, &pb_item);
+
+  ASSERT_TRUE(pb_item.has_ref());
+  EXPECT_EQ(pb_item.ref().id(), ref.id.value().id);
+  EXPECT_FALSE(pb_item.ref().has_is_dynamic());
+
+  std::unique_ptr<Item> item = DeserializeItemFromPb(pb_item, android::ResStringPool(),
+                                                     android::ConfigDescription(), nullptr,
+                                                     nullptr, nullptr);
+  Reference* actual_ref = ValueCast<Reference>(item.get());
+  EXPECT_EQ(actual_ref->id.value().id, ref.id.value().id);
+  EXPECT_FALSE(actual_ref->is_dynamic);
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 83e20b5..897fa80 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -245,7 +245,8 @@
   return package_map;
 }
 
-bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const {
+bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId,
+    const std::string& package_name) const {
   if (packageId == 0) {
     return true;
   }
@@ -253,7 +254,7 @@
   for (const std::unique_ptr<const ApkAssets>& assets : apk_assets_) {
     for (const std::unique_ptr<const android::LoadedPackage>& loaded_package
          : assets->GetLoadedArsc()->GetPackages()) {
-      if (packageId == loaded_package->GetPackageId() && loaded_package->IsDynamic()) {
+      if (package_name == loaded_package->GetPackageName() && loaded_package->IsDynamic()) {
         return true;
       }
     }
@@ -328,12 +329,12 @@
   bool found = false;
   ResourceId res_id = 0;
   uint32_t type_spec_flags;
+  ResourceName real_name;
 
   // There can be mangled resources embedded within other packages. Here we will
   // look into each package and look-up the mangled name until we find the resource.
   asset_manager_.ForEachPackage([&](const std::string& package_name, uint8_t id) -> bool {
-    ResourceName real_name(name.package, name.type, name.entry);
-
+    real_name = ResourceName(name.package, name.type, name.entry);
     if (package_name != name.package) {
       real_name.entry = mangled_entry;
       real_name.package = package_name;
@@ -353,12 +354,12 @@
   }
 
   std::unique_ptr<SymbolTable::Symbol> s;
-  if (name.type == ResourceType::kAttr) {
+  if (real_name.type == ResourceType::kAttr) {
     s = LookupAttributeInTable(asset_manager_, res_id);
   } else {
     s = util::make_unique<SymbolTable::Symbol>();
     s->id = res_id;
-    s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id());
+    s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package);
   }
 
   if (s) {
@@ -406,7 +407,7 @@
   } else {
     s = util::make_unique<SymbolTable::Symbol>();
     s->id = id;
-    s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id());
+    s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package);
   }
 
   if (s) {
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 6997cd6..06eaf63 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -194,7 +194,7 @@
 
   bool AddAssetPath(const android::StringPiece& path);
   std::map<size_t, std::string> GetAssignedPackageIds() const;
-  bool IsPackageDynamic(uint32_t packageId) const;
+  bool IsPackageDynamic(uint32_t packageId, const std::string& package_name) const;
 
   std::unique_ptr<SymbolTable::Symbol> FindByName(
       const ResourceName& name) override;
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index 0b82a3d..7bbac13 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -291,6 +291,15 @@
     }
 
     if (field->options().GetExtension(os::statsd::state_field_option).option() ==
+        os::statsd::StateField::PRIMARY_FIELD_FIRST_UID) {
+        if (javaType != JAVA_TYPE_ATTRIBUTION_CHAIN) {
+            errorCount++;
+        } else {
+            atomDecl->primaryFields.push_back(FIRST_UID_IN_CHAIN_ID);
+        }
+    }
+
+    if (field->options().GetExtension(os::statsd::state_field_option).option() ==
         os::statsd::StateField::EXCLUSIVE) {
         if (javaType == JAVA_TYPE_UNKNOWN ||
             javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 3efdd52..87d4d5d 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -36,6 +36,8 @@
 
 const int PULL_ATOM_START_ID = 10000;
 
+const int FIRST_UID_IN_CHAIN_ID = 0;
+
 /**
  * The types for atom parameters.
  */
diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp
index 54a9982..66ae964 100644
--- a/tools/stats_log_api_gen/atoms_info_writer.cpp
+++ b/tools/stats_log_api_gen/atoms_info_writer.cpp
@@ -25,6 +25,8 @@
 namespace stats_log_api_gen {
 
 static void write_atoms_info_header_body(FILE* out, const Atoms& atoms) {
+    fprintf(out, "static int FIRST_UID_IN_CHAIN = 0;\n");
+
     fprintf(out, "struct StateAtomFieldOptions {\n");
     fprintf(out, "  std::vector<int> primaryFields;\n");
     fprintf(out, "  int exclusiveField;\n");
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 180368c..6326f14 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -32,6 +32,7 @@
         // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
         // to a separate package.
         "java/android/net/wifi/WifiNetworkScoreCache.java",
+        "java/android/net/wifi/WifiOemConfigStoreMigrationHook.java",
         "java/android/net/wifi/wificond/*.java",
         ":libwificond_ipc_aidl",
     ],
@@ -51,18 +52,43 @@
     "//external/robolectric-shadows:__subpackages__",
 ]
 
+// wifi-service needs pre-jarjared version of framework-wifi so it can reference copied utility
+// classes before they are renamed.
 java_library {
-    name: "framework-wifi",
+    name: "framework-wifi-pre-jarjar",
     // TODO(b/140299412) should be core_current once we build against framework-system-stubs
     sdk_version: "core_platform",
+    static_libs: [
+        "framework-wifi-util-lib",
+        "android.hardware.wifi-V1.0-java-constants",
+    ],
     libs: [
         // TODO(b/140299412) should be framework-system-stubs once we fix all @hide dependencies
         "framework-minus-apex",
-        "unsupportedappusage",
+        "framework-annotations-lib",
+        "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
+        "unsupportedappusage-annotation", // for dalvik.annotation.compat.UnsupportedAppUsage
     ],
     srcs: [
         ":framework-wifi-updatable-sources",
     ],
+    installable: false,
+    visibility: [
+        "//frameworks/opt/net/wifi/service",
+        "//frameworks/opt/net/wifi/tests/wifitests",
+    ],
+}
+
+// post-jarjar version of framework-wifi
+java_library {
+    name: "framework-wifi",
+    // TODO(b/140299412) should be core_current once we build against framework-system-stubs
+    sdk_version: "core_platform",
+    static_libs: [
+        "framework-wifi-pre-jarjar",
+    ],
+    jarjar_rules: ":wifi-jarjar-rules",
+
     installable: true,
     optimize: {
         enabled: false
@@ -121,3 +147,8 @@
     ],
     visibility: test_access_hidden_api_whitelist,
 }
+
+filegroup {
+    name: "wifi-jarjar-rules",
+    srcs: ["jarjar-rules.txt"],
+}
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
new file mode 100644
index 0000000..8f72040
--- /dev/null
+++ b/wifi/jarjar-rules.txt
@@ -0,0 +1,40 @@
+rule android.net.InterfaceConfigurationParcel* @0
+rule android.net.InterfaceConfiguration* com.android.server.x.wifi.net.InterfaceConfiguration@1
+
+# We don't jar-jar the entire package because, we still use some classes (like
+# AsyncChannel in com.android.internal.util) from these packages which are not
+# inside our jar (currently in framework.jar, but will be in wifisdk.jar in the future).
+rule com.android.internal.util.FastXmlSerializer* com.android.server.x.wifi.util.FastXmlSerializer@1
+rule com.android.internal.util.HexDump* com.android.server.x.wifi.util.HexDump@1
+rule com.android.internal.util.IState* com.android.server.x.wifi.util.IState@1
+rule com.android.internal.util.MessageUtils* com.android.server.x.wifi.util.MessageUtils@1
+rule com.android.internal.util.State* com.android.server.x.wifi.util.State@1
+rule com.android.internal.util.StateMachine* com.android.server.x.wifi.util.StateMachine@1
+rule com.android.internal.util.WakeupMessage* com.android.server.x.wifi.util.WakeupMessage@1
+
+rule android.util.BackupUtils* com.android.server.x.wifi.util.BackupUtils@1
+rule android.util.LocalLog* com.android.server.x.wifi.util.LocalLog@1
+rule android.util.Rational* com.android.server.x.wifi.util.Rational@1
+
+rule android.os.BasicShellCommandHandler* com.android.server.x.wifi.os.BasicShellCommandHandler@1
+
+# Use our statically linked bouncy castle library
+rule org.bouncycastle.** com.android.server.x.wifi.bouncycastle.@1
+# Use our statically linked protobuf library
+rule com.google.protobuf.** com.android.server.x.wifi.protobuf.@1
+# use statically linked SystemMessageProto
+rule com.android.internal.messages.SystemMessageProto* com.android.server.x.wifi.messages.SystemMessageProto@1
+# Use our statically linked PlatformProperties library
+rule android.sysprop.** com.android.server.x.wifi.sysprop.@1
+
+
+# used by both framework-wifi and wifi-service
+rule android.content.pm.BaseParceledListSlice* android.x.net.wifi.util.BaseParceledListSlice@1
+rule android.content.pm.ParceledListSlice* android.x.net.wifi.util.ParceledListSlice@1
+rule android.net.shared.Inet4AddressUtils* android.x.net.wifi.util.Inet4AddressUtils@1
+rule android.os.HandlerExecutor* android.x.net.wifi.util.HandlerExecutor@1
+rule android.telephony.Annotation* android.x.net.wifi.util.TelephonyAnnotation@1
+rule com.android.internal.util.AsyncChannel* android.x.net.wifi.util.AsyncChannel@1
+rule com.android.internal.util.AsyncService* android.x.net.wifi.util.AsyncService@1
+rule com.android.internal.util.Preconditions* android.x.net.wifi.util.Preconditions@1
+rule com.android.internal.util.Protocol* android.x.net.wifi.util.Protocol@1
diff --git a/wifi/java/android/net/wifi/ISoftApCallback.aidl b/wifi/java/android/net/wifi/ISoftApCallback.aidl
index 482b491..f81bcb9 100644
--- a/wifi/java/android/net/wifi/ISoftApCallback.aidl
+++ b/wifi/java/android/net/wifi/ISoftApCallback.aidl
@@ -55,9 +55,17 @@
 
 
     /**
-     * Service to manager callback providing information of softap.
+     * Service to manager callback providing capability of softap.
      *
      * @param capability is the softap capability. {@link SoftApCapability}
      */
     void onCapabilityChanged(in SoftApCapability capability);
+
+    /**
+     * Service to manager callback providing blocked client of softap with specific reason code.
+     *
+     * @param client the currently blocked client.
+     * @param blockedReason one of blocked reason from {@link WifiManager.SapClientBlockedReason}
+     */
+    void onBlockedClientConnecting(in WifiClient client, int blockedReason);
 }
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 2b7f8af..a77d30a 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -32,6 +32,9 @@
 import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.CharsetEncoder;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
@@ -182,6 +185,22 @@
     private final @SecurityType int mSecurityType;
 
     /**
+     * The flag to indicate client need to authorize by user
+     * when client is connecting to AP.
+     */
+    private final boolean mClientControlByUser;
+
+    /**
+     * The list of blocked client that can't associate to the AP.
+     */
+    private final List<MacAddress> mBlockedClientList;
+
+    /**
+     * The list of allowed client that can associate to the AP.
+     */
+    private final List<MacAddress> mAllowedClientList;
+
+    /**
      * Delay in milliseconds before shutting down soft AP when
      * there are no connected devices.
      */
@@ -219,7 +238,9 @@
     /** Private constructor for Builder and Parcelable implementation. */
     private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
             @Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel,
-            @SecurityType int securityType, int maxNumberOfClients, int shutdownTimeoutMillis) {
+            @SecurityType int securityType, int maxNumberOfClients, int shutdownTimeoutMillis,
+            boolean clientControlByUser, @NonNull List<MacAddress> blockedList,
+            @NonNull List<MacAddress> allowedList) {
         mSsid = ssid;
         mBssid = bssid;
         mPassphrase = passphrase;
@@ -229,6 +250,9 @@
         mSecurityType = securityType;
         mMaxNumberOfClients = maxNumberOfClients;
         mShutdownTimeoutMillis = shutdownTimeoutMillis;
+        mClientControlByUser = clientControlByUser;
+        mBlockedClientList = new ArrayList<>(blockedList);
+        mAllowedClientList = new ArrayList<>(allowedList);
     }
 
     @Override
@@ -248,13 +272,17 @@
                 && mChannel == other.mChannel
                 && mSecurityType == other.mSecurityType
                 && mMaxNumberOfClients == other.mMaxNumberOfClients
-                && mShutdownTimeoutMillis == other.mShutdownTimeoutMillis;
+                && mShutdownTimeoutMillis == other.mShutdownTimeoutMillis
+                && mClientControlByUser == other.mClientControlByUser
+                && Objects.equals(mBlockedClientList, other.mBlockedClientList)
+                && Objects.equals(mAllowedClientList, other.mAllowedClientList);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid,
-                mBand, mChannel, mSecurityType, mMaxNumberOfClients, mShutdownTimeoutMillis);
+                mBand, mChannel, mSecurityType, mMaxNumberOfClients, mShutdownTimeoutMillis,
+                mClientControlByUser, mBlockedClientList, mAllowedClientList);
     }
 
     @Override
@@ -270,6 +298,9 @@
         sbuf.append(" \n SecurityType=").append(getSecurityType());
         sbuf.append(" \n MaxClient=").append(mMaxNumberOfClients);
         sbuf.append(" \n ShutdownTimeoutMillis=").append(mShutdownTimeoutMillis);
+        sbuf.append(" \n ClientControlByUser=").append(mClientControlByUser);
+        sbuf.append(" \n BlockedClientList=").append(mBlockedClientList);
+        sbuf.append(" \n AllowedClientList=").append(mAllowedClientList);
         return sbuf.toString();
     }
 
@@ -284,6 +315,9 @@
         dest.writeInt(mSecurityType);
         dest.writeInt(mMaxNumberOfClients);
         dest.writeInt(mShutdownTimeoutMillis);
+        dest.writeBoolean(mClientControlByUser);
+        dest.writeTypedList(mBlockedClientList);
+        dest.writeTypedList(mAllowedClientList);
     }
 
     @Override
@@ -299,7 +333,9 @@
                     in.readString(),
                     in.readParcelable(MacAddress.class.getClassLoader()),
                     in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(),
-                    in.readInt(), in.readInt());
+                    in.readInt(), in.readInt(), in.readBoolean(),
+                    in.createTypedArrayList(MacAddress.CREATOR),
+                    in.createTypedArrayList(MacAddress.CREATOR));
         }
 
         @Override
@@ -387,6 +423,34 @@
     }
 
     /**
+     * Returns a flag indicating whether clients need to be pre-approved by the user.
+     * (true: authorization required) or not (false: not required).
+     * {@link Builder#enableClientControlByUser(Boolean)}.
+     */
+    public boolean isClientControlByUserEnabled() {
+        return mClientControlByUser;
+    }
+
+    /**
+     * Returns List of clients which aren't allowed to associate to the AP.
+     *
+     * Clients are configured using {@link Builder#setClientList(List, List)}
+     */
+    @NonNull
+    public List<MacAddress> getBlockedClientList() {
+        return mBlockedClientList;
+    }
+
+    /**
+     * List of clients which are allowed to associate to the AP.
+     * Clients are configured using {@link Builder#setClientList(List, List)}
+     */
+    @NonNull
+    public List<MacAddress> getAllowedClientList() {
+        return mAllowedClientList;
+    }
+
+    /**
      * Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
      * Soft AP.
      *
@@ -403,6 +467,9 @@
         private int mMaxNumberOfClients;
         private int mSecurityType;
         private int mShutdownTimeoutMillis;
+        private boolean mClientControlByUser;
+        private List<MacAddress> mBlockedClientList;
+        private List<MacAddress> mAllowedClientList;
 
         /**
          * Constructs a Builder with default values (see {@link Builder}).
@@ -417,6 +484,9 @@
             mMaxNumberOfClients = 0;
             mSecurityType = SECURITY_TYPE_OPEN;
             mShutdownTimeoutMillis = 0;
+            mClientControlByUser = false;
+            mBlockedClientList = new ArrayList<>();
+            mAllowedClientList = new ArrayList<>();
         }
 
         /**
@@ -434,6 +504,9 @@
             mMaxNumberOfClients = other.mMaxNumberOfClients;
             mSecurityType = other.mSecurityType;
             mShutdownTimeoutMillis = other.mShutdownTimeoutMillis;
+            mClientControlByUser = other.mClientControlByUser;
+            mBlockedClientList = new ArrayList<>(other.mBlockedClientList);
+            mAllowedClientList = new ArrayList<>(other.mAllowedClientList);
         }
 
         /**
@@ -445,7 +518,8 @@
         public SoftApConfiguration build() {
             return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
                     mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients,
-                    mShutdownTimeoutMillis);
+                    mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList,
+                    mAllowedClientList);
         }
 
         /**
@@ -662,5 +736,82 @@
             mShutdownTimeoutMillis = timeoutMillis;
             return this;
         }
+
+        /**
+         * Configure the Soft AP to require manual user control of client association.
+         * If disabled (the default) then any client can associate to this Soft AP using the
+         * correct credentials until the Soft AP capacity is reached (capacity is hardware, carrier,
+         * or user limited - using {@link #setMaxNumberOfClients(int)}).
+         *
+         * If manual user control is enabled then clients will be accepted, rejected, or require
+         * a user approval based on the configuration provided by
+         * {@link #setClientList(List, List)}.
+         *
+         * <p>
+         * This method requires hardware support. Hardware support can be determined using
+         * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
+         * {@link SoftApCapability#isFeatureSupported(int)}
+         * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT}
+         *
+         * <p>
+         * If the method is called on a device without hardware support then starting the soft AP
+         * using {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will fail with
+         * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
+         *
+         * <p>
+         * <li>If not set, defaults to false (i.e The authoriztion is not required).</li>
+         *
+         * @param enabled true for enabling the control by user, false otherwise.
+         * @return Builder for chaining.
+         */
+        @NonNull
+        public Builder enableClientControlByUser(boolean enabled) {
+            mClientControlByUser = enabled;
+            return this;
+        }
+
+
+        /**
+         * This method together with {@link enableClientControlByUser(boolean)} control client
+         * connections to the AP. If {@link enableClientControlByUser(false)} is configured than
+         * this API has no effect and clients are allowed to associate to the AP (within limit of
+         * max number of clients).
+         *
+         * If {@link enableClientControlByUser(true)} is configured then this API configures
+         * 2 lists:
+         * <ul>
+         * <li>List of clients which are blocked. These are rejected.</li>
+         * <li>List of clients which are explicitly allowed. These are auto-accepted.</li>
+         * </ul>
+         *
+         * <p>
+         * All other clients which attempt to associate, whose MAC addresses are on neither list,
+         * are:
+         * <ul>
+         * <li>Rejected</li>
+         * <li>A callback {@link WifiManager.SoftApCallback#onBlockedClientConnecting(WifiClient)}
+         * is issued (which allows the user to add them to the allowed client list if desired).<li>
+         * </ul>
+         *
+         * @param blockedClientList list of clients which are not allowed to associate to the AP.
+         * @param allowedClientList list of clients which are allowed to associate to the AP
+         *                          without user pre-approval.
+         * @return Builder for chaining.
+         */
+        @NonNull
+        public Builder setClientList(@NonNull List<MacAddress> blockedClientList,
+                @NonNull List<MacAddress> allowedClientList) {
+            mBlockedClientList = new ArrayList<>(blockedClientList);
+            mAllowedClientList = new ArrayList<>(allowedClientList);
+            Iterator<MacAddress> iterator = mAllowedClientList.iterator();
+            while (iterator.hasNext()) {
+                MacAddress client = iterator.next();
+                int index = mBlockedClientList.indexOf(client);
+                if (index != -1) {
+                    throw new IllegalArgumentException("A MacAddress exist in both list");
+                }
+            }
+            return this;
+        }
     }
 }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 1baab12..a8a31eb 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -233,16 +233,20 @@
     public @interface SuggestionConnectionStatusCode {}
 
     /**
-     * Broadcast intent action indicating whether Wi-Fi scanning is allowed currently
-     * @hide
+     * Broadcast intent action indicating whether Wi-Fi scanning is currently available.
+     * Available extras:
+     * - {@link #EXTRA_SCAN_AVAILABLE}
      */
-    public static final String WIFI_SCAN_AVAILABLE = "wifi_scan_available";
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_WIFI_SCAN_AVAILABLE =
+            "android.net.wifi.action.WIFI_SCAN_AVAILABLE";
 
     /**
-     * Extra int indicating scan availability, WIFI_STATE_ENABLED and WIFI_STATE_DISABLED
-     * @hide
+     * A boolean extra indicating whether scanning is currently available.
+     * Sent in the broadcast {@link #ACTION_WIFI_SCAN_AVAILABLE}.
+     * Its value is true if scanning is currently available, false otherwise.
      */
-    public static final String EXTRA_SCAN_AVAILABLE = "scan_enabled";
+    public static final String EXTRA_SCAN_AVAILABLE = "android.net.wifi.extra.SCAN_AVAILABLE";
 
     /**
      * Broadcast intent action indicating that the credential of a Wi-Fi network
@@ -666,7 +670,8 @@
     public @interface SapStartFailure {}
 
     /**
-     *  All other reasons for AP start failure besides {@link #SAP_START_FAILURE_NO_CHANNEL}.
+     *  All other reasons for AP start failure besides {@link #SAP_START_FAILURE_NO_CHANNEL} and
+     *  {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
      *
      *  @hide
      */
@@ -691,6 +696,37 @@
     @SystemApi
     public static final int SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION = 2;
 
+
+    /** @hide */
+    @IntDef(flag = false, prefix = { "SAP_CLIENT_BLOCKED_REASON_" }, value = {
+        SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER,
+        SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SapClientBlockedReason {}
+
+    /**
+     *  If Soft Ap client is blocked, this reason code means that client doesn't exist in the
+     *  specified configuration {@link SoftApConfiguration.Builder#setClientList(List, List)}
+     *  and the {@link SoftApConfiguration.Builder#enableClientControlByUser(true)}
+     *  is configured as well.
+     *  @hide
+     */
+    @SystemApi
+    public static final int SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER = 0;
+
+    /**
+     *  If Soft Ap client is blocked, this reason code means that no more clients can be
+     *  associated to this AP since it reached maximum capacity. The maximum capacity is
+     *  the minimum of {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)} and
+     *  {@link SoftApCapability#getMaxSupportedClients} which get from
+     *  {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)}.
+     *
+     *  @hide
+     */
+    @SystemApi
+    public static final int SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS = 1;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"IFACE_IP_MODE_"}, value = {
@@ -3229,8 +3265,17 @@
     /**
      * Sets the Wi-Fi AP Configuration.
      *
+     * If the API is called while the soft AP is enabled, the configuration will apply to
+     * the current soft AP if the new configuration only includes
+     * {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)}
+     * or {@link SoftApConfiguration.Builder#setShutdownTimeoutMillis(int)}
+     * or {@link SoftApConfiguration.Builder#enableClientControlByUser(boolean)}
+     * or {@link SoftApConfiguration.Builder#setClientList(List, List)}.
+     *
+     * Otherwise, the configuration changes will be applied when the Soft AP is next started
+     * (the framework will not stop/start the AP).
+     *
      * @param softApConfig  A valid SoftApConfiguration specifying the configuration of the SAP.
-
      * @return {@code true} if the operation succeeded, {@code false} otherwise
      *
      * @hide
@@ -3460,7 +3505,8 @@
          *                      {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
          * @param failureReason reason when in failed state. One of
          *                      {@link #SAP_START_FAILURE_GENERAL},
-         *                      {@link #SAP_START_FAILURE_NO_CHANNEL}
+         *                      {@link #SAP_START_FAILURE_NO_CHANNEL},
+         *                      {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}
          */
         default void onStateChanged(@WifiApState int state, @SapStartFailure int failureReason) {}
 
@@ -3489,6 +3535,22 @@
             // Do nothing: can be updated to add SoftApCapability details (e.g. meximum supported
             // client number) to the UI.
         }
+
+        /**
+         * Called when client trying to connect but device blocked the client with specific reason.
+         *
+         * Can be used to ask user to update client to allowed list or blocked list
+         * when reason is {@link SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER}, or
+         * indicate the block due to maximum supported client number limitation when reason is
+         * {@link SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS}.
+         *
+         * @param client the currently blocked client.
+         * @param blockedReason one of blocked reason from {@link SapClientBlockedReason}
+         */
+        default void onBlockedClientConnecting(@NonNull WifiClient client,
+                @SapClientBlockedReason int blockedReason) {
+            // Do nothing: can be used to ask user to update client to allowed list or blocked list.
+        }
     }
 
     /**
@@ -3555,6 +3617,19 @@
                 mCallback.onCapabilityChanged(capability);
             });
         }
+
+        @Override
+        public void onBlockedClientConnecting(@NonNull WifiClient client, int blockedReason) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "SoftApCallbackProxy: onBlockedClientConnecting: client=" + client
+                        + " with reason = " + blockedReason);
+            }
+
+            Binder.clearCallingIdentity();
+            mExecutor.execute(() -> {
+                mCallback.onBlockedClientConnecting(client, blockedReason);
+            });
+        }
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/WifiOemConfigStoreMigrationHook.java b/wifi/java/android/net/wifi/WifiOemConfigStoreMigrationHook.java
new file mode 100755
index 0000000..642dcb9
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiOemConfigStoreMigrationHook.java
@@ -0,0 +1,180 @@
+/*
+ * 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.wifi;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * Class used to provide one time hooks for existing OEM devices to migrate their config store
+ * data to the wifi mainline module.
+ * <p>
+ * Note:
+ * <li> OEM's need to implement {@link #load()} only if their
+ * existing config store format or file locations differs from the vanilla AOSP implementation (
+ * which is what the wifi mainline module understands).
+ * </li>
+ * <li> The wifi mainline module will invoke {@link #load()}  method on every bootup, its
+ * the responsibility of the OEM implementation to ensure that this method returns non-null data
+ * only on the first bootup. Once the migration is done, the OEM can safely delete their config
+ * store files and then return null on any subsequent reboots. The first & only relevant invocation
+ * of {@link #load()} occurs when a previously released device upgrades to the wifi
+ * mainline module from an OEM implementation of the wifi stack.
+ * </li>
+ * @hide
+ */
+@SystemApi
+public final class WifiOemConfigStoreMigrationHook {
+    /**
+     * Container for all the wifi config data to migrate.
+     */
+    public static final class MigrationData implements Parcelable {
+        /**
+         * Builder to create instance of {@link MigrationData}.
+         */
+        public static final class Builder {
+            private List<WifiConfiguration> mUserSavedNetworkConfigurations;
+            private SoftApConfiguration mUserSoftApConfiguration;
+
+            public Builder() {
+                mUserSavedNetworkConfigurations = null;
+                mUserSoftApConfiguration = null;
+            }
+
+            /**
+             * Sets the list of all user's saved network configurations parsed from OEM config
+             * store files.
+             *
+             * @param userSavedNetworkConfigurations List of {@link WifiConfiguration} representing
+             *                                       the list of user's saved networks
+             * @return Instance of {@link Builder} to enable chaining of the builder method.
+             */
+            public @NonNull Builder setUserSavedNetworkConfigurations(
+                    @NonNull List<WifiConfiguration> userSavedNetworkConfigurations) {
+                checkNotNull(userSavedNetworkConfigurations);
+                mUserSavedNetworkConfigurations = userSavedNetworkConfigurations;
+                return this;
+            }
+
+            /**
+             * Sets the user's softap configuration parsed from OEM config store files.
+             *
+             * @param userSoftApConfiguration {@link SoftApConfiguration} representing user's
+             *                                SoftAp configuration
+             * @return Instance of {@link Builder} to enable chaining of the builder method.
+             */
+            public @NonNull Builder setUserSoftApConfiguration(
+                    @NonNull SoftApConfiguration userSoftApConfiguration) {
+                checkNotNull(userSoftApConfiguration);
+                mUserSoftApConfiguration  = userSoftApConfiguration;
+                return this;
+            }
+
+            /**
+             * Build an instance of {@link MigrationData}.
+             *
+             * @return Instance of {@link MigrationData}.
+             */
+            public @NonNull MigrationData build() {
+                return new MigrationData(mUserSavedNetworkConfigurations, mUserSoftApConfiguration);
+            }
+        }
+
+        private final List<WifiConfiguration> mUserSavedNetworkConfigurations;
+        private final SoftApConfiguration mUserSoftApConfiguration;
+
+        private MigrationData(
+                @Nullable List<WifiConfiguration> userSavedNetworkConfigurations,
+                @Nullable SoftApConfiguration userSoftApConfiguration) {
+            mUserSavedNetworkConfigurations = userSavedNetworkConfigurations;
+            mUserSoftApConfiguration = userSoftApConfiguration;
+        }
+
+        public static final @NonNull Parcelable.Creator<MigrationData> CREATOR =
+                new Parcelable.Creator<MigrationData>() {
+                    @Override
+                    public MigrationData createFromParcel(Parcel in) {
+                        List<WifiConfiguration> userSavedNetworkConfigurations =
+                                in.readArrayList(null);
+                        SoftApConfiguration userSoftApConfiguration = in.readParcelable(null);
+                        return new MigrationData(
+                                userSavedNetworkConfigurations, userSoftApConfiguration);
+                    }
+
+                    @Override
+                    public MigrationData[] newArray(int size) {
+                        return new MigrationData[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeList(mUserSavedNetworkConfigurations);
+            dest.writeParcelable(mUserSoftApConfiguration, flags);
+        }
+
+        /**
+         * Returns list of all user's saved network configurations.
+         *
+         * Note: Only to be returned if there is any format change in how OEM persisted this info.
+         * @return List of {@link WifiConfiguration} representing the list of user's saved networks,
+         * or null if no migration necessary.
+         */
+        @Nullable
+        public List<WifiConfiguration> getUserSavedNetworkConfigurations() {
+            return mUserSavedNetworkConfigurations;
+        }
+
+        /**
+         * Returns user's softap configuration.
+         *
+         * Note: Only to be returned if there is any format change in how OEM persisted this info.
+         * @return {@link SoftApConfiguration} representing user's SoftAp configuration,
+         * or null if no migration necessary.
+         */
+        @Nullable
+        public SoftApConfiguration getUserSoftApConfiguration() {
+            return mUserSoftApConfiguration;
+        }
+    }
+
+    private WifiOemConfigStoreMigrationHook() { }
+
+    /**
+     * Load data from OEM's config store.
+     *
+     * @return Instance of {@link MigrationData} for migrating data, null if no
+     * migration is necessary.
+     */
+    @Nullable
+    public static MigrationData load() {
+        // Note: OEM's should add code to parse data from their config store format here!
+        return null;
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 2c39c32a..4f602fa 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -833,6 +833,7 @@
      * delivered to the listener. It is possible that onFullResult will not be called for all
      * results of the first scan if the listener was registered during the scan.
      *
+     * @param executor the Executor on which to run the callback.
      * @param listener specifies the object to report events to. This object is also treated as a
      *                 key for this request, and must also be specified to cancel the request.
      *                 Multiple requests should also not share this object.
@@ -955,15 +956,32 @@
      * starts a single scan and reports results asynchronously
      * @param settings specifies various parameters for the scan; for more information look at
      * {@link ScanSettings}
-     * @param workSource WorkSource to blame for power usage
      * @param listener specifies the object to report events to. This object is also treated as a
      *                 key for this scan, and must also be specified to cancel the scan. Multiple
      *                 scans should also not share this object.
+     * @param workSource WorkSource to blame for power usage
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) {
+        startScan(settings, null, listener, workSource);
+    }
+
+    /**
+     * starts a single scan and reports results asynchronously
+     * @param settings specifies various parameters for the scan; for more information look at
+     * {@link ScanSettings}
+     * @param executor the Executor on which to run the callback.
+     * @param listener specifies the object to report events to. This object is also treated as a
+     *                 key for this scan, and must also be specified to cancel the scan. Multiple
+     *                 scans should also not share this object.
+     * @param workSource WorkSource to blame for power usage
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+    public void startScan(ScanSettings settings, @Nullable @CallbackExecutor Executor executor,
+            ScanListener listener, WorkSource workSource) {
         Objects.requireNonNull(listener, "listener cannot be null");
-        int key = addListener(listener);
+        int key = addListener(listener, executor);
         if (key == INVALID_KEY) return;
         validateChannel();
         Bundle scanParams = new Bundle();
@@ -1029,16 +1047,17 @@
      * {@link ScanSettings}
      * @param pnoSettings specifies various parameters for PNO; for more information look at
      * {@link PnoSettings}
+     * @param executor the Executor on which to run the callback.
      * @param listener specifies the object to report events to. This object is also treated as a
      *                 key for this scan, and must also be specified to cancel the scan. Multiple
      *                 scans should also not share this object.
      * {@hide}
      */
     public void startConnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
-            PnoScanListener listener) {
+            @NonNull @CallbackExecutor Executor executor, PnoScanListener listener) {
         Objects.requireNonNull(listener, "listener cannot be null");
         Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null");
-        int key = addListener(listener);
+        int key = addListener(listener, executor);
         if (key == INVALID_KEY) return;
         validateChannel();
         pnoSettings.isConnected = true;
@@ -1057,10 +1076,10 @@
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
-            PnoScanListener listener) {
+            @NonNull @CallbackExecutor Executor executor, PnoScanListener listener) {
         Objects.requireNonNull(listener, "listener cannot be null");
         Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null");
-        int key = addListener(listener);
+        int key = addListener(listener, executor);
         if (key == INVALID_KEY) return;
         validateChannel();
         pnoSettings.isConnected = false;
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index eeea7e2..6884a4e 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -25,6 +25,8 @@
 
 import org.junit.Test;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Random;
 
 @SmallTest
@@ -112,12 +114,18 @@
 
     @Test
     public void testWpa2WithAllFieldCustomized() {
+        List<MacAddress> testBlockedClientList = new ArrayList<>();
+        List<MacAddress> testAllowedClientList = new ArrayList<>();
+        testBlockedClientList.add(MacAddress.fromString("11:22:33:44:55:66"));
+        testAllowedClientList.add(MacAddress.fromString("aa:bb:cc:dd:ee:ff"));
         SoftApConfiguration original = new SoftApConfiguration.Builder()
                 .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                 .setChannel(149, SoftApConfiguration.BAND_5GHZ)
                 .setHiddenSsid(true)
                 .setMaxNumberOfClients(10)
                 .setShutdownTimeoutMillis(500000)
+                .enableClientControlByUser(true)
+                .setClientList(testBlockedClientList, testAllowedClientList)
                 .build();
         assertThat(original.getPassphrase()).isEqualTo("secretsecret");
         assertThat(original.getSecurityType()).isEqualTo(
@@ -127,6 +135,9 @@
         assertThat(original.isHiddenSsid()).isEqualTo(true);
         assertThat(original.getMaxNumberOfClients()).isEqualTo(10);
         assertThat(original.getShutdownTimeoutMillis()).isEqualTo(500000);
+        assertThat(original.isClientControlByUserEnabled()).isEqualTo(true);
+        assertThat(original.getBlockedClientList()).isEqualTo(testBlockedClientList);
+        assertThat(original.getAllowedClientList()).isEqualTo(testAllowedClientList);
 
         SoftApConfiguration unparceled = parcelUnparcel(original);
         assertThat(unparceled).isNotSameAs(original);
@@ -238,4 +249,17 @@
                 .setShutdownTimeoutMillis(-1)
                 .build();
     }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testsetClientListExceptionWhenExistMacAddressInBothList() {
+        final MacAddress testMacAddress_1 = MacAddress.fromString("22:33:44:55:66:77");
+        final MacAddress testMacAddress_2 = MacAddress.fromString("aa:bb:cc:dd:ee:ff");
+        ArrayList<MacAddress> testAllowedClientList = new ArrayList<>();
+        testAllowedClientList.add(testMacAddress_1);
+        testAllowedClientList.add(testMacAddress_2);
+        ArrayList<MacAddress> testBlockedClientList = new ArrayList<>();
+        testBlockedClientList.add(testMacAddress_1);
+        SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
+        configBuilder.setClientList(testBlockedClientList, testAllowedClientList);
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 4b83718..5bdc344 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -70,6 +70,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.net.DhcpInfo;
+import android.net.MacAddress;
 import android.net.wifi.WifiManager.LocalOnlyHotspotCallback;
 import android.net.wifi.WifiManager.LocalOnlyHotspotObserver;
 import android.net.wifi.WifiManager.LocalOnlyHotspotReservation;
@@ -897,6 +898,25 @@
     }
 
     /*
+     * Verify client-provided callback is being called through callback proxy
+     */
+    @Test
+    public void softApCallbackProxyCallsOnBlockedClientConnecting() throws Exception {
+        WifiClient testWifiClient = new WifiClient(MacAddress.fromString("22:33:44:55:66:77"));
+        ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
+                ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
+        mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback);
+        verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
+                anyInt());
+
+        callbackCaptor.getValue().onBlockedClientConnecting(testWifiClient,
+                WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS);
+        mLooper.dispatchAll();
+        verify(mSoftApCallback).onBlockedClientConnecting(testWifiClient,
+                WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS);
+    }
+
+    /*
      * Verify client-provided callback is being called through callback proxy on multiple events
      */
     @Test
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index 1af0bcb..0cc76b6 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -366,7 +366,7 @@
 
     /**
      * Test behavior of {@link WifiScanner#startDisconnectedPnoScan(ScanSettings, PnoSettings,
-     * WifiScanner.PnoScanListener)}
+     * Executor, WifiScanner.PnoScanListener)}
      * @throws Exception
      */
     @Test
@@ -375,7 +375,8 @@
         PnoSettings pnoSettings = new PnoSettings();
         WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class);
 
-        mWifiScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, pnoScanListener);
+        mWifiScanner.startDisconnectedPnoScan(
+                scanSettings, pnoSettings, mock(Executor.class), pnoScanListener);
         mLooper.dispatchAll();
 
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
@@ -396,7 +397,7 @@
 
     /**
      * Test behavior of {@link WifiScanner#startConnectedPnoScan(ScanSettings, PnoSettings,
-     * WifiScanner.PnoScanListener)}
+     * Executor, WifiScanner.PnoScanListener)}
      * @throws Exception
      */
     @Test
@@ -405,7 +406,8 @@
         PnoSettings pnoSettings = new PnoSettings();
         WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class);
 
-        mWifiScanner.startConnectedPnoScan(scanSettings, pnoSettings, pnoScanListener);
+        mWifiScanner.startConnectedPnoScan(
+                scanSettings, pnoSettings, mock(Executor.class), pnoScanListener);
         mLooper.dispatchAll();
 
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
@@ -426,7 +428,7 @@
 
     /**
      * Test behavior of {@link WifiScanner#stopPnoScan(ScanListener)}
-     * WifiScanner.PnoScanListener)}
+     * Executor, WifiScanner.PnoScanListener)}
      * @throws Exception
      */
     @Test
@@ -435,7 +437,8 @@
         PnoSettings pnoSettings = new PnoSettings();
         WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class);
 
-        mWifiScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, pnoScanListener);
+        mWifiScanner.startDisconnectedPnoScan(
+                scanSettings, pnoSettings, mock(Executor.class), pnoScanListener);
         mLooper.dispatchAll();
         mWifiScanner.stopPnoScan(pnoScanListener);
         mLooper.dispatchAll();